Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e080e0c0 authored by qctecmdr Service's avatar qctecmdr Service Committed by Gerrit - the friendly Code Review server
Browse files

Merge "wil6210: off channel transmit management frames in AP mode"

parents 65ec7035 10391fb0
Loading
Loading
Loading
Loading
+42 −8
Original line number Diff line number Diff line
@@ -943,11 +943,12 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,

	wil_dbg_misc(wil, "scan: wdev=0x%p iftype=%d\n", wdev, wdev->iftype);

	/* check we are client side */
	/* scan is supported on client interfaces and on AP interface */
	switch (wdev->iftype) {
	case NL80211_IFTYPE_STATION:
	case NL80211_IFTYPE_P2P_CLIENT:
	case NL80211_IFTYPE_P2P_DEVICE:
	case NL80211_IFTYPE_AP:
		break;
	default:
		return -EOPNOTSUPP;
@@ -1353,18 +1354,51 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
	int rc;
	bool tx_status;

	/* Note, currently we do not support the "wait" parameter, user-space
	 * must call remain_on_channel before mgmt_tx or listen on a channel
	 * another way (AP/PCP or connected station)
	 * in addition we need to check if specified "chan" argument is
	 * different from currently "listened" channel and fail if it is.
	wil_dbg_misc(wil, "mgmt_tx: channel %d offchan %d, wait %d\n",
		     params->chan ? params->chan->hw_value : -1,
		     params->offchan,
		     params->wait);

	/* Note, currently we support the "wait" parameter only on AP mode.
	 * In other modes, user-space must call remain_on_channel before
	 * mgmt_tx or listen on a channel other than active one.
	 */

	if (params->chan && params->chan->hw_value == 0) {
		wil_err(wil, "invalid channel\n");
		return -EINVAL;
	}

	if (wdev->iftype != NL80211_IFTYPE_AP) {
		wil_dbg_misc(wil,
			     "send WMI_SW_TX_REQ_CMDID on non-AP interfaces\n");
		rc = wmi_mgmt_tx(vif, buf, len);
	tx_status = (rc == 0);
		goto out;
	}

	if (!params->chan || params->chan->hw_value == vif->channel) {
		wil_dbg_misc(wil,
			     "send WMI_SW_TX_REQ_CMDID for on-channel\n");
		rc = wmi_mgmt_tx(vif, buf, len);
		goto out;
	}

	if (params->offchan == 0) {
		wil_err(wil,
			"invalid channel params: current %d requested %d, off-channel not allowed\n",
			vif->channel, params->chan->hw_value);
		return -EBUSY;
	}

	/* use the wmi_mgmt_tx_ext only on AP mode and off-channel */
	rc = wmi_mgmt_tx_ext(vif, buf, len, params->chan->hw_value,
			     params->wait);

out:
	tx_status = (rc == 0);
	cfg80211_mgmt_tx_status(wdev, cookie ? *cookie : 0, buf, len,
				tx_status, GFP_KERNEL);

	return rc;
}

+348 −3
Original line number Diff line number Diff line
@@ -1433,7 +1433,7 @@ static const struct file_operations fops_ssid = {
};

/*---------temp------------*/
static void print_temp(struct seq_file *s, const char *prefix, u32 t)
static void print_temp(struct seq_file *s, const char *prefix, s32 t)
{
	switch (t) {
	case 0:
@@ -1441,7 +1441,8 @@ static void print_temp(struct seq_file *s, const char *prefix, u32 t)
		seq_printf(s, "%s N/A\n", prefix);
	break;
	default:
		seq_printf(s, "%s %d.%03d\n", prefix, t / 1000, t % 1000);
		seq_printf(s, "%s %s%d.%03d\n", prefix, (t < 0 ? "-" : ""),
			   abs(t / 1000), abs(t % 1000));
		break;
	}
}
@@ -1449,7 +1450,7 @@ static void print_temp(struct seq_file *s, const char *prefix, u32 t)
static int wil_temp_debugfs_show(struct seq_file *s, void *data)
{
	struct wil6210_priv *wil = s->private;
	u32 t_m, t_r;
	s32 t_m, t_r;
	int rc = wmi_get_temperature(wil, &t_m, &t_r);

	if (rc) {
@@ -1840,6 +1841,343 @@ static const struct file_operations fops_mids = {
	.llseek		= seq_lseek,
};

static int wil_tx_latency_debugfs_show(struct seq_file *s, void *data)
__acquires(&p->tid_rx_lock) __releases(&p->tid_rx_lock)
{
	struct wil6210_priv *wil = s->private;
	int i, bin;

	for (i = 0; i < ARRAY_SIZE(wil->sta); i++) {
		struct wil_sta_info *p = &wil->sta[i];
		char *status = "unknown";
		u8 aid = 0;
		u8 mid;

		if (!p->tx_latency_bins)
			continue;

		switch (p->status) {
		case wil_sta_unused:
			status = "unused   ";
			break;
		case wil_sta_conn_pending:
			status = "pending  ";
			break;
		case wil_sta_connected:
			status = "connected";
			aid = p->aid;
			break;
		}
		mid = (p->status != wil_sta_unused) ? p->mid : U8_MAX;
		seq_printf(s, "[%d] %pM %s MID %d AID %d\n", i, p->addr, status,
			   mid, aid);

		if (p->status == wil_sta_connected) {
			u64 num_packets = 0;
			u64 tx_latency_avg = p->stats.tx_latency_total_us;

			seq_puts(s, "Tx/Latency bin:");
			for (bin = 0; bin < WIL_NUM_LATENCY_BINS; bin++) {
				seq_printf(s, " %lld",
					   p->tx_latency_bins[bin]);
				num_packets += p->tx_latency_bins[bin];
			}
			seq_puts(s, "\n");
			if (!num_packets)
				continue;
			do_div(tx_latency_avg, num_packets);
			seq_printf(s, "Tx/Latency min/avg/max (us): %d/%lld/%d",
				   p->stats.tx_latency_min_us,
				   tx_latency_avg,
				   p->stats.tx_latency_max_us);

			seq_puts(s, "\n");
		}
	}

	return 0;
}

static int wil_tx_latency_seq_open(struct inode *inode, struct file *file)
{
	return single_open(file, wil_tx_latency_debugfs_show,
			   inode->i_private);
}

static ssize_t wil_tx_latency_write(struct file *file, const char __user *buf,
				    size_t len, loff_t *ppos)
{
	struct seq_file *s = file->private_data;
	struct wil6210_priv *wil = s->private;
	int val, rc, i;
	bool enable;

	rc = kstrtoint_from_user(buf, len, 0, &val);
	if (rc) {
		wil_err(wil, "Invalid argument\n");
		return rc;
	}
	if (val == 1)
		/* default resolution */
		val = 500;
	if (val && (val < 50 || val > 1000)) {
		wil_err(wil, "Invalid resolution %d\n", val);
		return -EINVAL;
	}

	enable = !!val;
	if (wil->tx_latency == enable)
		return len;

	wil_info(wil, "%s TX latency measurements (resolution %dusec)\n",
		 enable ? "Enabling" : "Disabling", val);

	if (enable) {
		size_t sz = sizeof(u64) * WIL_NUM_LATENCY_BINS;

		wil->tx_latency_res = val;
		for (i = 0; i < ARRAY_SIZE(wil->sta); i++) {
			struct wil_sta_info *sta = &wil->sta[i];

			kfree(sta->tx_latency_bins);
			sta->tx_latency_bins = kzalloc(sz, GFP_KERNEL);
			if (!sta->tx_latency_bins)
				return -ENOMEM;
			sta->stats.tx_latency_min_us = U32_MAX;
			sta->stats.tx_latency_max_us = 0;
			sta->stats.tx_latency_total_us = 0;
		}
	}
	wil->tx_latency = enable;

	return len;
}

static const struct file_operations fops_tx_latency = {
	.open		= wil_tx_latency_seq_open,
	.release	= single_release,
	.read		= seq_read,
	.write		= wil_tx_latency_write,
	.llseek		= seq_lseek,
};

static void wil_link_stats_print_basic(struct wil6210_vif *vif,
				       struct seq_file *s,
				       struct wmi_link_stats_basic *basic)
{
	char per[5] = "?";

	if (basic->per_average != 0xff)
		snprintf(per, sizeof(per), "%d%%", basic->per_average);

	seq_printf(s, "CID %d {\n"
		   "\tTxMCS %d TxTpt %d\n"
		   "\tGoodput(rx:tx) %d:%d\n"
		   "\tRxBcastFrames %d\n"
		   "\tRSSI %d SQI %d SNR %d PER %s\n"
		   "\tRx RFC %d Ant num %d\n"
		   "\tSectors(rx:tx) my %d:%d peer %d:%d\n"
		   "}\n",
		   basic->cid,
		   basic->bf_mcs, le32_to_cpu(basic->tx_tpt),
		   le32_to_cpu(basic->rx_goodput),
		   le32_to_cpu(basic->tx_goodput),
		   le32_to_cpu(basic->rx_bcast_frames),
		   basic->rssi, basic->sqi, basic->snr, per,
		   basic->selected_rfc, basic->rx_effective_ant_num,
		   basic->my_rx_sector, basic->my_tx_sector,
		   basic->other_rx_sector, basic->other_tx_sector);
}

static void wil_link_stats_print_global(struct wil6210_priv *wil,
					struct seq_file *s,
					struct wmi_link_stats_global *global)
{
	seq_printf(s, "Frames(rx:tx) %d:%d\n"
		   "BA Frames(rx:tx) %d:%d\n"
		   "Beacons %d\n"
		   "Rx Errors (MIC:CRC) %d:%d\n"
		   "Tx Errors (no ack) %d\n",
		   le32_to_cpu(global->rx_frames),
		   le32_to_cpu(global->tx_frames),
		   le32_to_cpu(global->rx_ba_frames),
		   le32_to_cpu(global->tx_ba_frames),
		   le32_to_cpu(global->tx_beacons),
		   le32_to_cpu(global->rx_mic_errors),
		   le32_to_cpu(global->rx_crc_errors),
		   le32_to_cpu(global->tx_fail_no_ack));
}

static void wil_link_stats_debugfs_show_vif(struct wil6210_vif *vif,
					    struct seq_file *s)
{
	struct wil6210_priv *wil = vif_to_wil(vif);
	struct wmi_link_stats_basic *stats;
	int i;

	if (!vif->fw_stats_ready) {
		seq_puts(s, "no statistics\n");
		return;
	}

	seq_printf(s, "TSF %lld\n", vif->fw_stats_tsf);
	for (i = 0; i < ARRAY_SIZE(wil->sta); i++) {
		if (wil->sta[i].status == wil_sta_unused)
			continue;
		if (wil->sta[i].mid != vif->mid)
			continue;

		stats = &wil->sta[i].fw_stats_basic;
		wil_link_stats_print_basic(vif, s, stats);
	}
}

static int wil_link_stats_debugfs_show(struct seq_file *s, void *data)
{
	struct wil6210_priv *wil = s->private;
	struct wil6210_vif *vif;
	int i, rc;

	rc = mutex_lock_interruptible(&wil->vif_mutex);
	if (rc)
		return rc;

	/* iterate over all MIDs and show per-cid statistics. Then show the
	 * global statistics
	 */
	for (i = 0; i < wil->max_vifs; i++) {
		vif = wil->vifs[i];

		seq_printf(s, "MID %d ", i);
		if (!vif) {
			seq_puts(s, "unused\n");
			continue;
		}

		wil_link_stats_debugfs_show_vif(vif, s);
	}

	mutex_unlock(&wil->vif_mutex);

	return 0;
}

static int wil_link_stats_seq_open(struct inode *inode, struct file *file)
{
	return single_open(file, wil_link_stats_debugfs_show, inode->i_private);
}

static ssize_t wil_link_stats_write(struct file *file, const char __user *buf,
				    size_t len, loff_t *ppos)
{
	struct seq_file *s = file->private_data;
	struct wil6210_priv *wil = s->private;
	int cid, interval, rc, i;
	struct wil6210_vif *vif;
	char *kbuf = kmalloc(len + 1, GFP_KERNEL);

	if (!kbuf)
		return -ENOMEM;

	rc = simple_write_to_buffer(kbuf, len, ppos, buf, len);
	if (rc != len) {
		kfree(kbuf);
		return rc >= 0 ? -EIO : rc;
	}

	kbuf[len] = '\0';
	/* specify cid (use -1 for all cids) and snapshot interval in ms */
	rc = sscanf(kbuf, "%d %d", &cid, &interval);
	kfree(kbuf);
	if (rc < 0)
		return rc;
	if (rc < 2 || interval < 0)
		return -EINVAL;

	wil_info(wil, "request link statistics, cid %d interval %d\n",
		 cid, interval);

	rc = mutex_lock_interruptible(&wil->vif_mutex);
	if (rc)
		return rc;

	for (i = 0; i < wil->max_vifs; i++) {
		vif = wil->vifs[i];
		if (!vif)
			continue;

		rc = wmi_link_stats_cfg(vif, WMI_LINK_STATS_TYPE_BASIC,
					(cid == -1 ? 0xff : cid), interval);
		if (rc)
			wil_err(wil, "link statistics failed for mid %d\n", i);
	}
	mutex_unlock(&wil->vif_mutex);

	return len;
}

static const struct file_operations fops_link_stats = {
	.open		= wil_link_stats_seq_open,
	.release	= single_release,
	.read		= seq_read,
	.write		= wil_link_stats_write,
	.llseek		= seq_lseek,
};

static int
wil_link_stats_global_debugfs_show(struct seq_file *s, void *data)
{
	struct wil6210_priv *wil = s->private;

	if (!wil->fw_stats_global.ready)
		return 0;

	seq_printf(s, "TSF %lld\n", wil->fw_stats_global.tsf);
	wil_link_stats_print_global(wil, s, &wil->fw_stats_global.stats);

	return 0;
}

static int
wil_link_stats_global_seq_open(struct inode *inode, struct file *file)
{
	return single_open(file, wil_link_stats_global_debugfs_show,
			   inode->i_private);
}

static ssize_t
wil_link_stats_global_write(struct file *file, const char __user *buf,
			    size_t len, loff_t *ppos)
{
	struct seq_file *s = file->private_data;
	struct wil6210_priv *wil = s->private;
	int interval, rc;
	struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);

	/* specify snapshot interval in ms */
	rc = kstrtoint_from_user(buf, len, 0, &interval);
	if (rc || interval < 0) {
		wil_err(wil, "Invalid argument\n");
		return -EINVAL;
	}

	wil_info(wil, "request global link stats, interval %d\n", interval);

	rc = wmi_link_stats_cfg(vif, WMI_LINK_STATS_TYPE_GLOBAL, 0, interval);
	if (rc)
		wil_err(wil, "global link stats failed %d\n", rc);

	return rc ? rc : len;
}

static const struct file_operations fops_link_stats_global = {
	.open		= wil_link_stats_global_seq_open,
	.release	= single_release,
	.read		= seq_read,
	.write		= wil_link_stats_global_write,
	.llseek		= seq_lseek,
};

static ssize_t wil_read_file_led_cfg(struct file *file, char __user *user_buf,
				     size_t count, loff_t *ppos)
{
@@ -2174,6 +2512,9 @@ static const struct {
	{"srings",	0444,		&fops_srings},
	{"status_msg",	0444,		&fops_status_msg},
	{"rx_buff_mgmt",	0444,	&fops_rx_buff_mgmt},
	{"tx_latency",	0644,		&fops_tx_latency},
	{"link_stats",	0644,		&fops_link_stats},
	{"link_stats_global",	0644,	&fops_link_stats_global},
};

static void wil6210_debugfs_init_files(struct wil6210_priv *wil,
@@ -2292,10 +2633,14 @@ int wil6210_debugfs_init(struct wil6210_priv *wil)

void wil6210_debugfs_remove(struct wil6210_priv *wil)
{
	int i;

	debugfs_remove_recursive(wil->debug);
	wil->debug = NULL;

	kfree(wil->dbg_data.data_arr);
	for (i = 0; i < ARRAY_SIZE(wil->sta); i++)
		kfree(wil->sta[i].tx_latency_bins);

	/* free pmc memory without sending command to fw, as it will
	 * be reset on the way down anyway
+5 −1
Original line number Diff line number Diff line
@@ -281,6 +281,7 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
	}
	/* statistics */
	memset(&sta->stats, 0, sizeof(sta->stats));
	sta->stats.tx_latency_min_us = U32_MAX;
}

static bool wil_vif_is_connected(struct wil6210_priv *wil, u8 mid)
@@ -1133,6 +1134,9 @@ void wil_refresh_fw_capabilities(struct wil6210_priv *wil)
		wiphy->max_sched_scan_plans = WMI_MAX_PLANS_NUM;
	}

	if (test_bit(WMI_FW_CAPABILITY_TX_REQ_EXT, wil->fw_capabilities))
		wiphy->flags |= WIPHY_FLAG_OFFCHAN_TX;

	if (wil->platform_ops.set_features) {
		features = (test_bit(WMI_FW_CAPABILITY_REF_CLOCK_CONTROL,
				     wil->fw_capabilities) &&
@@ -1330,7 +1334,7 @@ static int wil_get_otp_info(struct wil6210_priv *wil)

static int wil_wait_for_fw_ready(struct wil6210_priv *wil)
{
	ulong to = msecs_to_jiffies(1000);
	ulong to = msecs_to_jiffies(2000);
	ulong left = wait_for_completion_timeout(&wil->wmi_ready, to);

	if (0 == left) {
+38 −0
Original line number Diff line number Diff line
@@ -1735,6 +1735,11 @@ static int __wil_tx_vring_tso(struct wil6210_priv *wil, struct wil6210_vif *vif,
	 */
	wmb();

	if (wil->tx_latency)
		*(ktime_t *)&skb->cb = ktime_get();
	else
		memset(skb->cb, 0, sizeof(ktime_t));

	wil_w(wil, vring->hwtail, vring->swhead);
	return 0;

@@ -1886,6 +1891,11 @@ static int __wil_tx_ring(struct wil6210_priv *wil, struct wil6210_vif *vif,
	 */
	wmb();

	if (wil->tx_latency)
		*(ktime_t *)&skb->cb = ktime_get();
	else
		memset(skb->cb, 0, sizeof(ktime_t));

	wil_w(wil, ring->hwtail, ring->swhead);

	return 0;
@@ -2108,6 +2118,31 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
	return NET_XMIT_DROP;
}

void wil_tx_latency_calc(struct wil6210_priv *wil, struct sk_buff *skb,
			 struct wil_sta_info *sta)
{
	int skb_time_us;
	int bin;

	if (!wil->tx_latency)
		return;

	if (ktime_to_ms(*(ktime_t *)&skb->cb) == 0)
		return;

	skb_time_us = ktime_us_delta(ktime_get(), *(ktime_t *)&skb->cb);
	bin = skb_time_us / wil->tx_latency_res;
	bin = min_t(int, bin, WIL_NUM_LATENCY_BINS - 1);

	wil_dbg_txrx(wil, "skb time %dus => bin %d\n", skb_time_us, bin);
	sta->tx_latency_bins[bin]++;
	sta->stats.tx_latency_total_us += skb_time_us;
	if (skb_time_us < sta->stats.tx_latency_min_us)
		sta->stats.tx_latency_min_us = skb_time_us;
	if (skb_time_us > sta->stats.tx_latency_max_us)
		sta->stats.tx_latency_max_us = skb_time_us;
}

/**
 * Clean up transmitted skb's from the Tx VRING
 *
@@ -2194,6 +2229,9 @@ int wil_tx_complete(struct wil6210_vif *vif, int ringid)
					if (stats) {
						stats->tx_packets++;
						stats->tx_bytes += skb->len;

						wil_tx_latency_calc(wil, skb,
							&wil->sta[cid]);
					}
				} else {
					ndev->stats.tx_errors++;
+2 −0
Original line number Diff line number Diff line
@@ -620,5 +620,7 @@ void wil_tid_ampdu_rx_free(struct wil6210_priv *wil,
			   struct wil_tid_ampdu_rx *r);
void wil_tx_data_init(struct wil_ring_tx_data *txdata);
void wil_init_txrx_ops_legacy_dma(struct wil6210_priv *wil);
void wil_tx_latency_calc(struct wil6210_priv *wil, struct sk_buff *skb,
			 struct wil_sta_info *sta);

#endif /* WIL6210_TXRX_H */
Loading