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

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.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 1f21b7fe
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@ wfx-y := \
	queue.o \
	data_tx.o \
	data_rx.o \
	scan.o \
	sta.o \
	main.o \
	sta.o \
+1 −1
Original line number Diff line number Diff line
@@ -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;
	}
+13 −0
Original line number Diff line number Diff line
@@ -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 },
+5 −0
Original line number Diff line number Diff line
@@ -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);
}
+249 −0
Original line number Diff line number Diff line
// 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);
	}
}
Loading