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

staging: wfx: allow to scan networks

Chip can make foreground scan or background, but both can't be mixed in
same request. So, we need to split each mac80211 requests into multiple
HIF requests.
Signed-off-by: default avatarJérôme Pouiller <jerome.pouiller@silabs.com>
Link: https://lore.kernel.org/r/20190919142527.31797-19-Jerome.Pouiller@silabs.comSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 1f21b7fe
......@@ -12,6 +12,7 @@ wfx-y := \
queue.o \
data_tx.o \
data_rx.o \
scan.o \
sta.o \
main.o \
sta.o \
......
......@@ -268,7 +268,7 @@ static void bh_work(struct work_struct *work)
if (last_op_is_rx)
ack_sdio_data(wdev);
if (!wdev->hif.tx_buffers_used && !work_pending(work)) {
if (!wdev->hif.tx_buffers_used && !work_pending(work) && !atomic_read(&wdev->scan_in_progress)) {
device_release(wdev);
release_chip = true;
}
......
......@@ -11,6 +11,7 @@
#include "hif_rx.h"
#include "wfx.h"
#include "scan.h"
#include "data_rx.h"
#include "secure_link.h"
#include "hif_api_cmd.h"
......@@ -143,6 +144,17 @@ static int hif_receive_indication(struct wfx_dev *wdev, struct hif_msg *hif, voi
return 0;
}
static int hif_scan_complete_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf)
{
struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface);
struct hif_ind_scan_cmpl *body = buf;
WARN_ON(!wvif);
wfx_scan_complete_cb(wvif, body);
return 0;
}
static int hif_join_complete_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf)
{
struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface);
......@@ -230,6 +242,7 @@ static const struct {
{ HIF_IND_ID_STARTUP, hif_startup_indication },
{ HIF_IND_ID_WAKEUP, hif_wakeup_indication },
{ HIF_IND_ID_JOIN_COMPLETE, hif_join_complete_indication },
{ HIF_IND_ID_SCAN_CMPL, hif_scan_complete_indication },
{ HIF_IND_ID_SL_EXCHANGE_PUB_KEYS, hif_keys_indication },
{ HIF_IND_ID_GENERIC, hif_generic_indication },
{ HIF_IND_ID_ERROR, hif_error_indication },
......
......@@ -55,6 +55,7 @@ static const struct ieee80211_ops wfx_ops = {
.add_interface = wfx_add_interface,
.remove_interface = wfx_remove_interface,
.tx = wfx_tx,
.hw_scan = wfx_hw_scan,
};
bool wfx_api_older_than(struct wfx_dev *wdev, int major, int minor)
......@@ -203,6 +204,8 @@ struct wfx_dev *wfx_init_common(struct device *dev,
hw->extra_tx_headroom = sizeof(struct hif_sl_msg_hdr) + sizeof(struct hif_msg)
+ sizeof(struct hif_req_tx)
+ 4 /* alignment */ + 8 /* TKIP IV */;
hw->wiphy->max_scan_ssids = 2;
hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
wdev = hw->priv;
wdev->hw = hw;
......@@ -214,6 +217,7 @@ struct wfx_dev *wfx_init_common(struct device *dev,
wdev->pdata.gpio_wakeup = wfx_get_gpio(dev, gpio_wakeup, "wakeup");
wfx_fill_sl_key(dev, &wdev->pdata);
mutex_init(&wdev->conf_mutex);
mutex_init(&wdev->rx_stats_lock);
init_completion(&wdev->firmware_ready);
wfx_init_hif_cmd(&wdev->hif_cmd);
......@@ -225,6 +229,7 @@ struct wfx_dev *wfx_init_common(struct device *dev,
void wfx_free_common(struct wfx_dev *wdev)
{
mutex_destroy(&wdev->rx_stats_lock);
mutex_destroy(&wdev->conf_mutex);
wfx_tx_queues_deinit(wdev);
ieee80211_free_hw(wdev->hw);
}
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Scan related functions.
*
* Copyright (c) 2017-2019, Silicon Laboratories, Inc.
* Copyright (c) 2010, ST-Ericsson
*/
#include <net/mac80211.h>
#include "scan.h"
#include "wfx.h"
#include "sta.h"
#include "hif_tx_mib.h"
static void __ieee80211_scan_completed_compat(struct ieee80211_hw *hw, bool aborted)
{
struct cfg80211_scan_info info = {
.aborted = aborted ? 1 : 0,
};
ieee80211_scan_completed(hw, &info);
}
static int wfx_scan_start(struct wfx_vif *wvif, struct wfx_scan_params *scan)
{
int ret;
int tmo = 500;
tmo += scan->scan_req.num_of_channels *
((20 * (scan->scan_req.max_channel_time)) + 10);
atomic_set(&wvif->scan.in_progress, 1);
atomic_set(&wvif->wdev->scan_in_progress, 1);
schedule_delayed_work(&wvif->scan.timeout, msecs_to_jiffies(tmo));
ret = hif_scan(wvif, scan);
if (ret) {
wfx_scan_failed_cb(wvif);
atomic_set(&wvif->scan.in_progress, 0);
atomic_set(&wvif->wdev->scan_in_progress, 0);
cancel_delayed_work_sync(&wvif->scan.timeout);
}
return ret;
}
int wfx_hw_scan(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_scan_request *hw_req)
{
struct wfx_dev *wdev = hw->priv;
struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv;
struct cfg80211_scan_request *req = &hw_req->req;
struct sk_buff *skb;
int i, ret;
struct hif_mib_template_frame *p;
if (!wvif)
return -EINVAL;
if (req->n_ssids == 1 && !req->ssids[0].ssid_len)
req->n_ssids = 0;
if (req->n_ssids > HIF_API_MAX_NB_SSIDS)
return -EINVAL;
skb = ieee80211_probereq_get(hw, wvif->vif->addr, NULL, 0, req->ie_len);
if (!skb)
return -ENOMEM;
if (req->ie_len)
memcpy(skb_put(skb, req->ie_len), req->ie, req->ie_len);
mutex_lock(&wdev->conf_mutex);
p = (struct hif_mib_template_frame *)skb_push(skb, 4);
p->frame_type = HIF_TMPLT_PRBREQ;
p->frame_length = cpu_to_le16(skb->len - 4);
ret = hif_set_template_frame(wvif, p);
skb_pull(skb, 4);
if (!ret)
/* Host want to be the probe responder. */
ret = wfx_fwd_probe_req(wvif, true);
if (ret) {
mutex_unlock(&wdev->conf_mutex);
dev_kfree_skb(skb);
return ret;
}
wfx_tx_lock_flush(wdev);
BUG_ON(wvif->scan.req);
wvif->scan.req = req;
wvif->scan.n_ssids = 0;
wvif->scan.status = 0;
wvif->scan.begin = &req->channels[0];
wvif->scan.curr = wvif->scan.begin;
wvif->scan.end = &req->channels[req->n_channels];
wvif->scan.output_power = wdev->output_power;
for (i = 0; i < req->n_ssids; ++i) {
struct hif_ssid_def *dst = &wvif->scan.ssids[wvif->scan.n_ssids];
memcpy(&dst->ssid[0], req->ssids[i].ssid, sizeof(dst->ssid));
dst->ssid_length = req->ssids[i].ssid_len;
++wvif->scan.n_ssids;
}
mutex_unlock(&wdev->conf_mutex);
if (skb)
dev_kfree_skb(skb);
schedule_work(&wvif->scan.work);
return 0;
}
void wfx_scan_work(struct work_struct *work)
{
struct wfx_vif *wvif = container_of(work, struct wfx_vif, scan.work);
struct ieee80211_channel **it;
struct wfx_scan_params scan = {
.scan_req.scan_type.type = 0, /* Foreground */
};
struct ieee80211_channel *first;
int i;
down(&wvif->scan.lock);
mutex_lock(&wvif->wdev->conf_mutex);
if (!wvif->scan.req || wvif->scan.curr == wvif->scan.end) {
if (wvif->scan.output_power != wvif->wdev->output_power)
hif_set_output_power(wvif, wvif->wdev->output_power * 10);
if (wvif->scan.status < 0)
dev_warn(wvif->wdev->dev, "scan failed\n");
else if (wvif->scan.req)
dev_dbg(wvif->wdev->dev, "scan completed\n");
else
dev_dbg(wvif->wdev->dev, "scan canceled\n");
wvif->scan.req = NULL;
wfx_tx_unlock(wvif->wdev);
mutex_unlock(&wvif->wdev->conf_mutex);
__ieee80211_scan_completed_compat(wvif->wdev->hw, wvif->scan.status ? 1 : 0);
up(&wvif->scan.lock);
return;
}
first = *wvif->scan.curr;
for (it = wvif->scan.curr + 1, i = 1;
it != wvif->scan.end && i < HIF_API_MAX_NB_CHANNELS;
++it, ++i) {
if ((*it)->band != first->band)
break;
if (((*it)->flags ^ first->flags) &
IEEE80211_CHAN_NO_IR)
break;
if (!(first->flags & IEEE80211_CHAN_NO_IR) &&
(*it)->max_power != first->max_power)
break;
}
scan.scan_req.band = first->band;
if (wvif->scan.req->no_cck)
scan.scan_req.max_transmit_rate = API_RATE_INDEX_G_6MBPS;
else
scan.scan_req.max_transmit_rate = API_RATE_INDEX_B_1MBPS;
scan.scan_req.num_of_probe_requests =
(first->flags & IEEE80211_CHAN_NO_IR) ? 0 : 2;
scan.scan_req.num_of_ssi_ds = wvif->scan.n_ssids;
scan.ssids = &wvif->scan.ssids[0];
scan.scan_req.num_of_channels = it - wvif->scan.curr;
scan.scan_req.probe_delay = 100;
scan.ch = kcalloc(scan.scan_req.num_of_channels, sizeof(u8), GFP_KERNEL);
if (!scan.ch) {
wvif->scan.status = -ENOMEM;
goto fail;
}
for (i = 0; i < scan.scan_req.num_of_channels; ++i)
scan.ch[i] = wvif->scan.curr[i]->hw_value;
if (wvif->scan.curr[0]->flags & IEEE80211_CHAN_NO_IR) {
scan.scan_req.min_channel_time = 50;
scan.scan_req.max_channel_time = 150;
} else {
scan.scan_req.min_channel_time = 10;
scan.scan_req.max_channel_time = 50;
}
if (!(first->flags & IEEE80211_CHAN_NO_IR) &&
wvif->scan.output_power != first->max_power) {
wvif->scan.output_power = first->max_power;
hif_set_output_power(wvif, wvif->scan.output_power * 10);
}
wvif->scan.status = wfx_scan_start(wvif, &scan);
kfree(scan.ch);
if (wvif->scan.status)
goto fail;
wvif->scan.curr = it;
mutex_unlock(&wvif->wdev->conf_mutex);
return;
fail:
wvif->scan.curr = wvif->scan.end;
mutex_unlock(&wvif->wdev->conf_mutex);
up(&wvif->scan.lock);
schedule_work(&wvif->scan.work);
}
static void wfx_scan_complete(struct wfx_vif *wvif)
{
up(&wvif->scan.lock);
atomic_set(&wvif->wdev->scan_in_progress, 0);
wfx_scan_work(&wvif->scan.work);
}
void wfx_scan_failed_cb(struct wfx_vif *wvif)
{
if (cancel_delayed_work_sync(&wvif->scan.timeout) > 0) {
wvif->scan.status = -EIO;
schedule_work(&wvif->scan.timeout.work);
}
}
void wfx_scan_complete_cb(struct wfx_vif *wvif, struct hif_ind_scan_cmpl *arg)
{
if (cancel_delayed_work_sync(&wvif->scan.timeout) > 0) {
wvif->scan.status = 1;
schedule_work(&wvif->scan.timeout.work);
}
}
void wfx_scan_timeout(struct work_struct *work)
{
struct wfx_vif *wvif = container_of(work, struct wfx_vif, scan.timeout.work);
if (atomic_xchg(&wvif->scan.in_progress, 0)) {
if (wvif->scan.status > 0) {
wvif->scan.status = 0;
} else if (!wvif->scan.status) {
dev_warn(wvif->wdev->dev, "timeout waiting for scan complete notification\n");
wvif->scan.status = -ETIMEDOUT;
wvif->scan.curr = wvif->scan.end;
hif_stop_scan(wvif);
}
wfx_scan_complete(wvif);
}
}
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Scan related functions.
*
* Copyright (c) 2017-2019, Silicon Laboratories, Inc.
* Copyright (c) 2010, ST-Ericsson
*/
#ifndef WFX_SCAN_H
#define WFX_SCAN_H
#include <linux/semaphore.h>
#include <linux/workqueue.h>
#include <net/mac80211.h>
#include "hif_api_cmd.h"
struct wfx_dev;
struct wfx_vif;
struct wfx_scan {
struct semaphore lock;
struct work_struct work;
struct delayed_work timeout;
struct cfg80211_scan_request *req;
struct ieee80211_channel **begin;
struct ieee80211_channel **curr;
struct ieee80211_channel **end;
struct hif_ssid_def ssids[HIF_API_MAX_NB_SSIDS];
int output_power;
int n_ssids;
int status;
atomic_t in_progress;
};
int wfx_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_scan_request *req);
void wfx_scan_work(struct work_struct *work);
void wfx_scan_timeout(struct work_struct *work);
void wfx_scan_complete_cb(struct wfx_vif *wvif, struct hif_ind_scan_cmpl *arg);
void wfx_scan_failed_cb(struct wfx_vif *wvif);
#endif /* WFX_SCAN_H */
......@@ -9,9 +9,18 @@
#include "sta.h"
#include "wfx.h"
#include "scan.h"
#include "hif_tx_mib.h"
#define TXOP_UNIT 32
int wfx_fwd_probe_req(struct wfx_vif *wvif, bool enable)
{
wvif->fwd_probe_req = enable;
return hif_set_rx_filter(wvif, wvif->filter_bssid,
wvif->fwd_probe_req);
}
static int wfx_set_tim_impl(struct wfx_vif *wvif, bool aid0_bit_set)
{
struct sk_buff *skb;
......@@ -128,6 +137,8 @@ int wfx_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
default_edca_params[IEEE80211_AC_BK].queue_id = HIF_QUEUE_ID_BESTEFFORT;
}
mutex_lock(&wdev->conf_mutex);
for (i = 0; i < ARRAY_SIZE(wdev->vif); i++) {
if (!wdev->vif[i]) {
wdev->vif[i] = vif;
......@@ -135,8 +146,10 @@ int wfx_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
break;
}
}
if (i == ARRAY_SIZE(wdev->vif))
if (i == ARRAY_SIZE(wdev->vif)) {
mutex_unlock(&wdev->conf_mutex);
return -EOPNOTSUPP;
}
wvif->vif = vif;
wvif->wdev = wdev;
......@@ -148,6 +161,12 @@ int wfx_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
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);
sema_init(&wvif->scan.lock, 1);
INIT_WORK(&wvif->scan.work, wfx_scan_work);
INIT_DELAYED_WORK(&wvif->scan.timeout, wfx_scan_timeout);
mutex_unlock(&wdev->conf_mutex);
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]));
......@@ -175,7 +194,9 @@ void wfx_stop(struct ieee80211_hw *hw)
struct wfx_dev *wdev = hw->priv;
wfx_tx_lock_flush(wdev);
mutex_lock(&wdev->conf_mutex);
wfx_tx_queues_clear(wdev);
mutex_unlock(&wdev->conf_mutex);
wfx_tx_unlock(wdev);
WARN(atomic_read(&wdev->tx_lock), "tx_lock is locked");
}
......@@ -12,6 +12,8 @@
#include "hif_api_cmd.h"
struct wfx_vif;
struct wfx_edca_params {
/* NOTE: index is a linux queue id. */
struct hif_req_edca_queue_params params[IEEE80211_NUM_ACS];
......@@ -29,4 +31,6 @@ void wfx_stop(struct ieee80211_hw *hw);
int wfx_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
void wfx_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
int wfx_fwd_probe_req(struct wfx_vif *wvif, bool enable);
#endif /* WFX_STA_H */
......@@ -19,6 +19,7 @@
#include "queue.h"
#include "secure_link.h"
#include "sta.h"
#include "scan.h"
#include "hif_tx.h"
#include "hif_api_general.h"
......@@ -39,6 +40,7 @@ struct wfx_dev {
struct wfx_hif hif;
struct sl_context sl;
int chip_frozen;
struct mutex conf_mutex;
struct wfx_hif_cmd hif_cmd;
struct wfx_queue tx_queue[4];
......@@ -48,6 +50,9 @@ struct wfx_dev {
struct hif_rx_stats rx_stats;
struct mutex rx_stats_lock;
int output_power;
atomic_t scan_in_progress;
};
struct wfx_vif {
......@@ -71,11 +76,17 @@ struct wfx_vif {
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;
bool filter_bssid;
bool fwd_probe_req;
struct wfx_edca_params edca;
struct wfx_scan scan;
};
static inline struct wfx_vif *wdev_to_wvif(struct wfx_dev *wdev, int vif_id)
......
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