Commit 398ce273 authored by Hector Martin's avatar Hector Martin Committed by Kalle Valo
Browse files

wifi: brcmfmac: cfg80211: Add support for scan params v2



This new API version is required for at least the BCM4387 firmware. Add
support for it, with a fallback to the v1 API.

Acked-by: default avatarLinus Walleij <linus.walleij@linaro.org>
Reviewed-by: default avatarArend van Spriel <arend.vanspriel@broadcom.com>
Signed-off-by: default avatarHector Martin <marcan@marcan.st>
Signed-off-by: default avatarKalle Valo <kvalo@kernel.org>
Link: https://lore.kernel.org/r/20230214092423.15175-3-marcan@marcan.st
parent 098e0b10
Loading
Loading
Loading
Loading
+157 −88
Original line number Diff line number Diff line
@@ -1039,12 +1039,134 @@ void brcmf_set_mpc(struct brcmf_if *ifp, int mpc)
	}
}

static void brcmf_scan_params_v2_to_v1(struct brcmf_scan_params_v2_le *params_v2_le,
				       struct brcmf_scan_params_le *params_le)
{
	size_t params_size;
	u32 ch;
	int n_channels, n_ssids;

	memcpy(&params_le->ssid_le, &params_v2_le->ssid_le,
	       sizeof(params_le->ssid_le));
	memcpy(&params_le->bssid, &params_v2_le->bssid,
	       sizeof(params_le->bssid));

	params_le->bss_type = params_v2_le->bss_type;
	params_le->scan_type = le32_to_cpu(params_v2_le->scan_type);
	params_le->nprobes = params_v2_le->nprobes;
	params_le->active_time = params_v2_le->active_time;
	params_le->passive_time = params_v2_le->passive_time;
	params_le->home_time = params_v2_le->home_time;
	params_le->channel_num = params_v2_le->channel_num;

	ch = le32_to_cpu(params_v2_le->channel_num);
	n_channels = ch & BRCMF_SCAN_PARAMS_COUNT_MASK;
	n_ssids = ch >> BRCMF_SCAN_PARAMS_NSSID_SHIFT;

	params_size = sizeof(u16) * n_channels;
	if (n_ssids > 0) {
		params_size = roundup(params_size, sizeof(u32));
		params_size += sizeof(struct brcmf_ssid_le) * n_ssids;
	}

	memcpy(&params_le->channel_list[0],
	       &params_v2_le->channel_list[0], params_size);
}

static void brcmf_escan_prep(struct brcmf_cfg80211_info *cfg,
			     struct brcmf_scan_params_v2_le *params_le,
			     struct cfg80211_scan_request *request)
{
	u32 n_ssids;
	u32 n_channels;
	s32 i;
	s32 offset;
	u16 chanspec;
	char *ptr;
	int length;
	struct brcmf_ssid_le ssid_le;

	eth_broadcast_addr(params_le->bssid);

	length = BRCMF_SCAN_PARAMS_V2_FIXED_SIZE;

	params_le->version = cpu_to_le16(BRCMF_SCAN_PARAMS_VERSION_V2);
	params_le->bss_type = DOT11_BSSTYPE_ANY;
	params_le->scan_type = cpu_to_le32(BRCMF_SCANTYPE_ACTIVE);
	params_le->channel_num = 0;
	params_le->nprobes = cpu_to_le32(-1);
	params_le->active_time = cpu_to_le32(-1);
	params_le->passive_time = cpu_to_le32(-1);
	params_le->home_time = cpu_to_le32(-1);
	memset(&params_le->ssid_le, 0, sizeof(params_le->ssid_le));

	/* Scan abort */
	if (!request) {
		length += sizeof(u16);
		params_le->channel_num = cpu_to_le32(1);
		params_le->channel_list[0] = cpu_to_le16(-1);
		params_le->length = cpu_to_le16(length);
		return;
	}

	n_ssids = request->n_ssids;
	n_channels = request->n_channels;

	/* Copy channel array if applicable */
	brcmf_dbg(SCAN, "### List of channelspecs to scan ### %d\n",
		  n_channels);
	if (n_channels > 0) {
		length += roundup(sizeof(u16) * n_channels, sizeof(u32));
		for (i = 0; i < n_channels; i++) {
			chanspec = channel_to_chanspec(&cfg->d11inf,
						       request->channels[i]);
			brcmf_dbg(SCAN, "Chan : %d, Channel spec: %x\n",
				  request->channels[i]->hw_value, chanspec);
			params_le->channel_list[i] = cpu_to_le16(chanspec);
		}
	} else {
		brcmf_dbg(SCAN, "Scanning all channels\n");
	}

	/* Copy ssid array if applicable */
	brcmf_dbg(SCAN, "### List of SSIDs to scan ### %d\n", n_ssids);
	if (n_ssids > 0) {
		offset = offsetof(struct brcmf_scan_params_v2_le, channel_list) +
				n_channels * sizeof(u16);
		offset = roundup(offset, sizeof(u32));
		length += sizeof(ssid_le) * n_ssids,
		ptr = (char *)params_le + offset;
		for (i = 0; i < n_ssids; i++) {
			memset(&ssid_le, 0, sizeof(ssid_le));
			ssid_le.SSID_len =
					cpu_to_le32(request->ssids[i].ssid_len);
			memcpy(ssid_le.SSID, request->ssids[i].ssid,
			       request->ssids[i].ssid_len);
			if (!ssid_le.SSID_len)
				brcmf_dbg(SCAN, "%d: Broadcast scan\n", i);
			else
				brcmf_dbg(SCAN, "%d: scan for  %.32s size=%d\n",
					  i, ssid_le.SSID, ssid_le.SSID_len);
			memcpy(ptr, &ssid_le, sizeof(ssid_le));
			ptr += sizeof(ssid_le);
		}
	} else {
		brcmf_dbg(SCAN, "Performing passive scan\n");
		params_le->scan_type = cpu_to_le32(BRCMF_SCANTYPE_PASSIVE);
	}
	params_le->length = cpu_to_le16(length);
	/* Adding mask to channel numbers */
	params_le->channel_num =
		cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) |
			(n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK));
}

s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
				struct brcmf_if *ifp, bool aborted,
				bool fw_abort)
{
	struct brcmf_pub *drvr = cfg->pub;
	struct brcmf_scan_params_le params_le;
	struct brcmf_scan_params_v2_le params_v2_le;
	struct cfg80211_scan_request *scan_request;
	u64 reqid;
	u32 bucket;
@@ -1063,20 +1185,23 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
	if (fw_abort) {
		/* Do a scan abort to stop the driver's scan engine */
		brcmf_dbg(SCAN, "ABORT scan in firmware\n");
		memset(&params_le, 0, sizeof(params_le));
		eth_broadcast_addr(params_le.bssid);
		params_le.bss_type = DOT11_BSSTYPE_ANY;
		params_le.scan_type = 0;
		params_le.channel_num = cpu_to_le32(1);
		params_le.nprobes = cpu_to_le32(1);
		params_le.active_time = cpu_to_le32(-1);
		params_le.passive_time = cpu_to_le32(-1);
		params_le.home_time = cpu_to_le32(-1);
		/* Scan is aborted by setting channel_list[0] to -1 */
		params_le.channel_list[0] = cpu_to_le16(-1);

		brcmf_escan_prep(cfg, &params_v2_le, NULL);

		/* E-Scan (or anyother type) can be aborted by SCAN */
		if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SCAN_V2)) {
			err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
						     &params_v2_le,
						     sizeof(params_v2_le));
		} else {
			struct brcmf_scan_params_le params_le;

			brcmf_scan_params_v2_to_v1(&params_v2_le, &params_le);
			err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
					     &params_le, sizeof(params_le));
						     &params_le,
						     sizeof(params_le));
		}

		if (err)
			bphy_err(drvr, "Scan abort failed\n");
	}
@@ -1295,83 +1420,13 @@ brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
	return err;
}

static void brcmf_escan_prep(struct brcmf_cfg80211_info *cfg,
			     struct brcmf_scan_params_le *params_le,
			     struct cfg80211_scan_request *request)
{
	u32 n_ssids;
	u32 n_channels;
	s32 i;
	s32 offset;
	u16 chanspec;
	char *ptr;
	struct brcmf_ssid_le ssid_le;

	eth_broadcast_addr(params_le->bssid);
	params_le->bss_type = DOT11_BSSTYPE_ANY;
	params_le->scan_type = BRCMF_SCANTYPE_ACTIVE;
	params_le->channel_num = 0;
	params_le->nprobes = cpu_to_le32(-1);
	params_le->active_time = cpu_to_le32(-1);
	params_le->passive_time = cpu_to_le32(-1);
	params_le->home_time = cpu_to_le32(-1);
	memset(&params_le->ssid_le, 0, sizeof(params_le->ssid_le));

	n_ssids = request->n_ssids;
	n_channels = request->n_channels;

	/* Copy channel array if applicable */
	brcmf_dbg(SCAN, "### List of channelspecs to scan ### %d\n",
		  n_channels);
	if (n_channels > 0) {
		for (i = 0; i < n_channels; i++) {
			chanspec = channel_to_chanspec(&cfg->d11inf,
						       request->channels[i]);
			brcmf_dbg(SCAN, "Chan : %d, Channel spec: %x\n",
				  request->channels[i]->hw_value, chanspec);
			params_le->channel_list[i] = cpu_to_le16(chanspec);
		}
	} else {
		brcmf_dbg(SCAN, "Scanning all channels\n");
	}
	/* Copy ssid array if applicable */
	brcmf_dbg(SCAN, "### List of SSIDs to scan ### %d\n", n_ssids);
	if (n_ssids > 0) {
		offset = offsetof(struct brcmf_scan_params_le, channel_list) +
				n_channels * sizeof(u16);
		offset = roundup(offset, sizeof(u32));
		ptr = (char *)params_le + offset;
		for (i = 0; i < n_ssids; i++) {
			memset(&ssid_le, 0, sizeof(ssid_le));
			ssid_le.SSID_len =
					cpu_to_le32(request->ssids[i].ssid_len);
			memcpy(ssid_le.SSID, request->ssids[i].ssid,
			       request->ssids[i].ssid_len);
			if (!ssid_le.SSID_len)
				brcmf_dbg(SCAN, "%d: Broadcast scan\n", i);
			else
				brcmf_dbg(SCAN, "%d: scan for  %.32s size=%d\n",
					  i, ssid_le.SSID, ssid_le.SSID_len);
			memcpy(ptr, &ssid_le, sizeof(ssid_le));
			ptr += sizeof(ssid_le);
		}
	} else {
		brcmf_dbg(SCAN, "Performing passive scan\n");
		params_le->scan_type = BRCMF_SCANTYPE_PASSIVE;
	}
	/* Adding mask to channel numbers */
	params_le->channel_num =
		cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) |
			(n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK));
}

static s32
brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp,
		struct cfg80211_scan_request *request)
{
	struct brcmf_pub *drvr = cfg->pub;
	s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE +
			  offsetof(struct brcmf_escan_params_le, params_le);
	s32 params_size = BRCMF_SCAN_PARAMS_V2_FIXED_SIZE +
			  offsetof(struct brcmf_escan_params_le, params_v2_le);
	struct brcmf_escan_params_le *params;
	s32 err = 0;

@@ -1391,8 +1446,22 @@ brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp,
		goto exit;
	}
	BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN);
	brcmf_escan_prep(cfg, &params->params_le, request);
	params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
	brcmf_escan_prep(cfg, &params->params_v2_le, request);

	params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION_V2);

	if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SCAN_V2)) {
		struct brcmf_escan_params_le *params_v1;

		params_size -= BRCMF_SCAN_PARAMS_V2_FIXED_SIZE;
		params_size += BRCMF_SCAN_PARAMS_FIXED_SIZE;
		params_v1 = kzalloc(params_size, GFP_KERNEL);
		params_v1->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
		brcmf_scan_params_v2_to_v1(&params->params_v2_le, &params_v1->params_le);
		kfree(params);
		params = params_v1;
	}

	params->action = cpu_to_le16(WL_ESCAN_ACTION_START);
	params->sync_id = cpu_to_le16(0x1234);

+1 −0
Original line number Diff line number Diff line
@@ -290,6 +290,7 @@ void brcmf_feat_attach(struct brcmf_pub *drvr)
		ifp->drvr->feat_flags |= BIT(BRCMF_FEAT_SCAN_RANDOM_MAC);

	brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_FWSUP, "sup_wpa");
	brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_SCAN_V2, "scan_ver");

	if (drvr->settings->feature_disable) {
		brcmf_dbg(INFO, "Features: 0x%02x, disable: 0x%02x\n",
+3 −1
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@
 * SAE: simultaneous authentication of equals
 * FWAUTH: Firmware authenticator
 * DUMP_OBSS: Firmware has capable to dump obss info to support ACS
 * SCAN_V2: Version 2 scan params
 */
#define BRCMF_FEAT_LIST \
	BRCMF_FEAT_DEF(MBSS) \
@@ -53,7 +54,8 @@
	BRCMF_FEAT_DEF(DOT11H) \
	BRCMF_FEAT_DEF(SAE) \
	BRCMF_FEAT_DEF(FWAUTH) \
	BRCMF_FEAT_DEF(DUMP_OBSS)
	BRCMF_FEAT_DEF(DUMP_OBSS) \
	BRCMF_FEAT_DEF(SCAN_V2)

/*
 * Quirks:
+48 −1
Original line number Diff line number Diff line
@@ -48,6 +48,10 @@

/* size of brcmf_scan_params not including variable length array */
#define BRCMF_SCAN_PARAMS_FIXED_SIZE	64
#define BRCMF_SCAN_PARAMS_V2_FIXED_SIZE	72

/* version of brcmf_scan_params structure */
#define BRCMF_SCAN_PARAMS_VERSION_V2	2

/* masks for channel and ssid count */
#define BRCMF_SCAN_PARAMS_COUNT_MASK	0x0000ffff
@@ -67,6 +71,7 @@
#define BRCMF_PRIMARY_KEY		(1 << 1)
#define DOT11_BSSTYPE_ANY		2
#define BRCMF_ESCAN_REQ_VERSION		1
#define BRCMF_ESCAN_REQ_VERSION_V2	2

#define BRCMF_MAXRATES_IN_SET		16	/* max # of rates in rateset */

@@ -386,6 +391,45 @@ struct brcmf_scan_params_le {
	__le16 channel_list[1];	/* list of chanspecs */
};

struct brcmf_scan_params_v2_le {
	__le16 version;		/* structure version */
	__le16 length;		/* structure length */
	struct brcmf_ssid_le ssid_le;	/* default: {0, ""} */
	u8 bssid[ETH_ALEN];	/* default: bcast */
	s8 bss_type;		/* default: any,
				 * DOT11_BSSTYPE_ANY/INFRASTRUCTURE/INDEPENDENT
				 */
	u8 pad;
	__le32 scan_type;	/* flags, 0 use default */
	__le32 nprobes;		/* -1 use default, number of probes per channel */
	__le32 active_time;	/* -1 use default, dwell time per channel for
				 * active scanning
				 */
	__le32 passive_time;	/* -1 use default, dwell time per channel
				 * for passive scanning
				 */
	__le32 home_time;	/* -1 use default, dwell time for the
				 * home channel between channel scans
				 */
	__le32 channel_num;	/* count of channels and ssids that follow
				 *
				 * low half is count of channels in
				 * channel_list, 0 means default (use all
				 * available channels)
				 *
				 * high half is entries in struct brcmf_ssid
				 * array that follows channel_list, aligned for
				 * s32 (4 bytes) meaning an odd channel count
				 * implies a 2-byte pad between end of
				 * channel_list and first ssid
				 *
				 * if ssid count is zero, single ssid in the
				 * fixed parameter portion is assumed, otherwise
				 * ssid in the fixed portion is ignored
				 */
	__le16 channel_list[1];	/* list of chanspecs */
};

struct brcmf_scan_results {
	u32 buflen;
	u32 version;
@@ -397,7 +441,10 @@ struct brcmf_escan_params_le {
	__le32 version;
	__le16 action;
	__le16 sync_id;
	union {
		struct brcmf_scan_params_le params_le;
		struct brcmf_scan_params_v2_le params_v2_le;
	};
};

struct brcmf_escan_result_le {