diff options
Diffstat (limited to 'drivers/net/wireless/ath/wil6210')
| -rw-r--r-- | drivers/net/wireless/ath/wil6210/Makefile | 1 | ||||
| -rw-r--r-- | drivers/net/wireless/ath/wil6210/cfg80211.c | 246 | ||||
| -rw-r--r-- | drivers/net/wireless/ath/wil6210/debugfs.c | 185 | ||||
| -rw-r--r-- | drivers/net/wireless/ath/wil6210/interrupt.c | 48 | ||||
| -rw-r--r-- | drivers/net/wireless/ath/wil6210/main.c | 275 | ||||
| -rw-r--r-- | drivers/net/wireless/ath/wil6210/netdev.c | 19 | ||||
| -rw-r--r-- | drivers/net/wireless/ath/wil6210/pcie_bus.c | 38 | ||||
| -rw-r--r-- | drivers/net/wireless/ath/wil6210/rx_reorder.c | 201 | ||||
| -rw-r--r-- | drivers/net/wireless/ath/wil6210/txrx.c | 368 | ||||
| -rw-r--r-- | drivers/net/wireless/ath/wil6210/txrx.h | 7 | ||||
| -rw-r--r-- | drivers/net/wireless/ath/wil6210/wil6210.h | 176 | ||||
| -rw-r--r-- | drivers/net/wireless/ath/wil6210/wmi.c | 172 | ||||
| -rw-r--r-- | drivers/net/wireless/ath/wil6210/wmi.h | 50 | 
13 files changed, 1516 insertions, 270 deletions
diff --git a/drivers/net/wireless/ath/wil6210/Makefile b/drivers/net/wireless/ath/wil6210/Makefile index 990dd42ae79..c7a3465fd02 100644 --- a/drivers/net/wireless/ath/wil6210/Makefile +++ b/drivers/net/wireless/ath/wil6210/Makefile @@ -9,6 +9,7 @@ wil6210-y += wmi.o  wil6210-y += interrupt.o  wil6210-y += txrx.o  wil6210-y += debug.o +wil6210-y += rx_reorder.o  wil6210-$(CONFIG_WIL6210_TRACING) += trace.o  # for tracing framework to find trace.h diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 61c302a6bde..820d4ebd932 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -104,41 +104,125 @@ int wil_iftype_nl2wmi(enum nl80211_iftype type)  	return -EOPNOTSUPP;  } -static int wil_cfg80211_get_station(struct wiphy *wiphy, -				    struct net_device *ndev, -				    u8 *mac, struct station_info *sinfo) +static int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid, +			      struct station_info *sinfo)  { -	struct wil6210_priv *wil = wiphy_to_wil(wiphy); -	int rc;  	struct wmi_notify_req_cmd cmd = { -		.cid = 0, +		.cid = cid,  		.interval_usec = 0,  	}; +	struct { +		struct wil6210_mbox_hdr_wmi wmi; +		struct wmi_notify_req_done_event evt; +	} __packed reply; +	struct wil_net_stats *stats = &wil->sta[cid].stats; +	int rc; -	if (memcmp(mac, wil->dst_addr[0], ETH_ALEN)) -		return -ENOENT; - -	/* WMI_NOTIFY_REQ_DONE_EVENTID handler fills wil->stats.bf_mcs */  	rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, &cmd, sizeof(cmd), -		      WMI_NOTIFY_REQ_DONE_EVENTID, NULL, 0, 20); +		      WMI_NOTIFY_REQ_DONE_EVENTID, &reply, sizeof(reply), 20);  	if (rc)  		return rc; +	wil_dbg_wmi(wil, "Link status for CID %d: {\n" +		    "  MCS %d TSF 0x%016llx\n" +		    "  BF status 0x%08x SNR 0x%08x SQI %d%%\n" +		    "  Tx Tpt %d goodput %d Rx goodput %d\n" +		    "  Sectors(rx:tx) my %d:%d peer %d:%d\n""}\n", +		    cid, le16_to_cpu(reply.evt.bf_mcs), +		    le64_to_cpu(reply.evt.tsf), reply.evt.status, +		    le32_to_cpu(reply.evt.snr_val), +		    reply.evt.sqi, +		    le32_to_cpu(reply.evt.tx_tpt), +		    le32_to_cpu(reply.evt.tx_goodput), +		    le32_to_cpu(reply.evt.rx_goodput), +		    le16_to_cpu(reply.evt.my_rx_sector), +		    le16_to_cpu(reply.evt.my_tx_sector), +		    le16_to_cpu(reply.evt.other_rx_sector), +		    le16_to_cpu(reply.evt.other_tx_sector)); +  	sinfo->generation = wil->sinfo_gen; -	sinfo->filled |= STATION_INFO_TX_BITRATE; +	sinfo->filled = STATION_INFO_RX_BYTES | +			STATION_INFO_TX_BYTES | +			STATION_INFO_RX_PACKETS | +			STATION_INFO_TX_PACKETS | +			STATION_INFO_RX_BITRATE | +			STATION_INFO_TX_BITRATE | +			STATION_INFO_RX_DROP_MISC | +			STATION_INFO_TX_FAILED; +  	sinfo->txrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G; -	sinfo->txrate.mcs = wil->stats.bf_mcs; -	sinfo->filled |= STATION_INFO_RX_BITRATE; +	sinfo->txrate.mcs = le16_to_cpu(reply.evt.bf_mcs);  	sinfo->rxrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G; -	sinfo->rxrate.mcs = wil->stats.last_mcs_rx; +	sinfo->rxrate.mcs = stats->last_mcs_rx; +	sinfo->rx_bytes = stats->rx_bytes; +	sinfo->rx_packets = stats->rx_packets; +	sinfo->rx_dropped_misc = stats->rx_dropped; +	sinfo->tx_bytes = stats->tx_bytes; +	sinfo->tx_packets = stats->tx_packets; +	sinfo->tx_failed = stats->tx_errors;  	if (test_bit(wil_status_fwconnected, &wil->status)) {  		sinfo->filled |= STATION_INFO_SIGNAL; -		sinfo->signal = 12; /* TODO: provide real value */ +		sinfo->signal = reply.evt.sqi;  	} -	return 0; +	return rc; +} + +static int wil_cfg80211_get_station(struct wiphy *wiphy, +				    struct net_device *ndev, +				    const u8 *mac, struct station_info *sinfo) +{ +	struct wil6210_priv *wil = wiphy_to_wil(wiphy); +	int rc; + +	int cid = wil_find_cid(wil, mac); + +	wil_dbg_misc(wil, "%s(%pM) CID %d\n", __func__, mac, cid); +	if (cid < 0) +		return cid; + +	rc = wil_cid_fill_sinfo(wil, cid, sinfo); + +	return rc; +} + +/* + * Find @idx-th active STA for station dump. + */ +static int wil_find_cid_by_idx(struct wil6210_priv *wil, int idx) +{ +	int i; + +	for (i = 0; i < ARRAY_SIZE(wil->sta); i++) { +		if (wil->sta[i].status == wil_sta_unused) +			continue; +		if (idx == 0) +			return i; +		idx--; +	} + +	return -ENOENT; +} + +static int wil_cfg80211_dump_station(struct wiphy *wiphy, +				     struct net_device *dev, int idx, +				     u8 *mac, struct station_info *sinfo) +{ +	struct wil6210_priv *wil = wiphy_to_wil(wiphy); +	int rc; +	int cid = wil_find_cid_by_idx(wil, idx); + +	if (cid < 0) +		return -ENOENT; + +	memcpy(mac, wil->sta[cid].addr, ETH_ALEN); +	wil_dbg_misc(wil, "%s(%pM) CID %d\n", __func__, mac, cid); + +	rc = wil_cid_fill_sinfo(wil, cid, sinfo); + +	return rc;  }  static int wil_cfg80211_change_iface(struct wiphy *wiphy, @@ -181,6 +265,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,  		u16 chnl[4];  	} __packed cmd;  	uint i, n; +	int rc;  	if (wil->scan_request) {  		wil_err(wil, "Already scanning\n"); @@ -198,11 +283,12 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,  	/* FW don't support scan after connection attempt */  	if (test_bit(wil_status_dontscan, &wil->status)) { -		wil_err(wil, "Scan after connect attempt not supported\n"); +		wil_err(wil, "Can't scan now\n");  		return -EBUSY;  	}  	wil->scan_request = request; +	mod_timer(&wil->scan_timer, jiffies + WIL6210_SCAN_TO);  	memset(&cmd, 0, sizeof(cmd));  	cmd.cmd.num_channels = 0; @@ -221,8 +307,13 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,  			     request->channels[i]->center_freq);  	} -	return wmi_send(wil, WMI_START_SCAN_CMDID, &cmd, sizeof(cmd.cmd) + +	rc = wmi_send(wil, WMI_START_SCAN_CMDID, &cmd, sizeof(cmd.cmd) +  			cmd.cmd.num_channels * sizeof(cmd.cmd.channel_list[0])); + +	if (rc) +		wil->scan_request = NULL; + +	return rc;  }  static int wil_cfg80211_connect(struct wiphy *wiphy, @@ -237,6 +328,10 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,  	int ch;  	int rc = 0; +	if (test_bit(wil_status_fwconnecting, &wil->status) || +	    test_bit(wil_status_fwconnected, &wil->status)) +		return -EALREADY; +  	bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid,  			       sme->ssid, sme->ssid_len,  			       WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); @@ -316,12 +411,9 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,  	}  	conn.channel = ch - 1; -	memcpy(conn.bssid, bss->bssid, 6); -	memcpy(conn.dst_mac, bss->bssid, 6); -	/* -	 * FW don't support scan after connection attempt -	 */ -	set_bit(wil_status_dontscan, &wil->status); +	memcpy(conn.bssid, bss->bssid, ETH_ALEN); +	memcpy(conn.dst_mac, bss->bssid, ETH_ALEN); +  	set_bit(wil_status_fwconnecting, &wil->status);  	rc = wmi_send(wil, WMI_CONNECT_CMDID, &conn, sizeof(conn)); @@ -330,7 +422,6 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,  		mod_timer(&wil->connect_timer,  			  jiffies + msecs_to_jiffies(2000));  	} else { -		clear_bit(wil_status_dontscan, &wil->status);  		clear_bit(wil_status_fwconnecting, &wil->status);  	} @@ -352,6 +443,40 @@ static int wil_cfg80211_disconnect(struct wiphy *wiphy,  	return rc;  } +static int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, +				struct wireless_dev *wdev, +				struct cfg80211_mgmt_tx_params *params, +				u64 *cookie) +{ +	const u8 *buf = params->buf; +	size_t len = params->len; +	struct wil6210_priv *wil = wiphy_to_wil(wiphy); +	int rc; +	struct ieee80211_mgmt *mgmt_frame = (void *)buf; +	struct wmi_sw_tx_req_cmd *cmd; +	struct { +		struct wil6210_mbox_hdr_wmi wmi; +		struct wmi_sw_tx_complete_event evt; +	} __packed evt; + +	cmd = kmalloc(sizeof(*cmd) + len, GFP_KERNEL); +	if (!cmd) +		return -ENOMEM; + +	memcpy(cmd->dst_mac, mgmt_frame->da, WMI_MAC_LEN); +	cmd->len = cpu_to_le16(len); +	memcpy(cmd->payload, buf, len); + +	rc = wmi_call(wil, WMI_SW_TX_REQ_CMDID, cmd, sizeof(*cmd) + len, +		      WMI_SW_TX_COMPLETE_EVENTID, &evt, sizeof(evt), 2000); +	if (rc == 0) +		rc = evt.evt.status; + +	kfree(cmd); + +	return rc; +} +  static int wil_cfg80211_set_channel(struct wiphy *wiphy,  				    struct cfg80211_chan_def *chandef)  { @@ -402,6 +527,41 @@ static int wil_cfg80211_set_default_key(struct wiphy *wiphy,  	return 0;  } +static int wil_remain_on_channel(struct wiphy *wiphy, +				 struct wireless_dev *wdev, +				 struct ieee80211_channel *chan, +				 unsigned int duration, +				 u64 *cookie) +{ +	struct wil6210_priv *wil = wiphy_to_wil(wiphy); +	int rc; + +	/* TODO: handle duration */ +	wil_info(wil, "%s(%d, %d ms)\n", __func__, chan->center_freq, duration); + +	rc = wmi_set_channel(wil, chan->hw_value); +	if (rc) +		return rc; + +	rc = wmi_rxon(wil, true); + +	return rc; +} + +static int wil_cancel_remain_on_channel(struct wiphy *wiphy, +					struct wireless_dev *wdev, +					u64 cookie) +{ +	struct wil6210_priv *wil = wiphy_to_wil(wiphy); +	int rc; + +	wil_info(wil, "%s()\n", __func__); + +	rc = wmi_rxon(wil, false); + +	return rc; +} +  static int wil_fix_bcon(struct wil6210_priv *wil,  			struct cfg80211_beacon_data *bcon)  { @@ -450,18 +610,20 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,  	if (wil_fix_bcon(wil, bcon))  		wil_dbg_misc(wil, "Fixed bcon\n"); +	mutex_lock(&wil->mutex); +  	rc = wil_reset(wil);  	if (rc) -		return rc; +		goto out;  	/* Rx VRING. */  	rc = wil_rx_init(wil);  	if (rc) -		return rc; +		goto out;  	rc = wmi_set_ssid(wil, info->ssid_len, info->ssid);  	if (rc) -		return rc; +		goto out;  	/* MAC address - pre-requisite for other commands */  	wmi_set_mac_address(wil, ndev->dev_addr); @@ -485,11 +647,13 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,  	rc = wmi_pcp_start(wil, info->beacon_interval, wmi_nettype,  			   channel->hw_value);  	if (rc) -		return rc; +		goto out;  	netif_carrier_on(ndev); +out: +	mutex_unlock(&wil->mutex);  	return rc;  } @@ -499,17 +663,36 @@ static int wil_cfg80211_stop_ap(struct wiphy *wiphy,  	int rc = 0;  	struct wil6210_priv *wil = wiphy_to_wil(wiphy); +	mutex_lock(&wil->mutex); +  	rc = wmi_pcp_stop(wil); +	mutex_unlock(&wil->mutex);  	return rc;  } +static int wil_cfg80211_del_station(struct wiphy *wiphy, +				    struct net_device *dev, const u8 *mac) +{ +	struct wil6210_priv *wil = wiphy_to_wil(wiphy); + +	mutex_lock(&wil->mutex); +	wil6210_disconnect(wil, mac); +	mutex_unlock(&wil->mutex); + +	return 0; +} +  static struct cfg80211_ops wil_cfg80211_ops = {  	.scan = wil_cfg80211_scan,  	.connect = wil_cfg80211_connect,  	.disconnect = wil_cfg80211_disconnect,  	.change_virtual_intf = wil_cfg80211_change_iface,  	.get_station = wil_cfg80211_get_station, +	.dump_station = wil_cfg80211_dump_station, +	.remain_on_channel = wil_remain_on_channel, +	.cancel_remain_on_channel = wil_cancel_remain_on_channel, +	.mgmt_tx = wil_cfg80211_mgmt_tx,  	.set_monitor_channel = wil_cfg80211_set_channel,  	.add_key = wil_cfg80211_add_key,  	.del_key = wil_cfg80211_del_key, @@ -517,6 +700,7 @@ static struct cfg80211_ops wil_cfg80211_ops = {  	/* AP mode */  	.start_ap = wil_cfg80211_start_ap,  	.stop_ap = wil_cfg80211_stop_ap, +	.del_station = wil_cfg80211_del_station,  };  static void wil_wiphy_init(struct wiphy *wiphy) @@ -542,7 +726,7 @@ static void wil_wiphy_init(struct wiphy *wiphy)  	wiphy->bands[IEEE80211_BAND_60GHZ] = &wil_band_60ghz;  	/* TODO: figure this out */ -	wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; +	wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC;  	wiphy->cipher_suites = wil_cipher_suites;  	wiphy->n_cipher_suites = ARRAY_SIZE(wil_cipher_suites); diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index 1caa31992a7..8d4bc4bfb66 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -26,14 +26,16 @@  /* Nasty hack. Better have per device instances */  static u32 mem_addr;  static u32 dbg_txdesc_index; +static u32 dbg_vring_index; /* 24+ for Rx, 0..23 for Tx */  static void wil_print_vring(struct seq_file *s, struct wil6210_priv *wil, -			    const char *name, struct vring *vring) +			    const char *name, struct vring *vring, +			    char _s, char _h)  {  	void __iomem *x = wmi_addr(wil, vring->hwtail);  	seq_printf(s, "VRING %s = {\n", name); -	seq_printf(s, "  pa     = 0x%016llx\n", (unsigned long long)vring->pa); +	seq_printf(s, "  pa     = %pad\n", &vring->pa);  	seq_printf(s, "  va     = 0x%p\n", vring->va);  	seq_printf(s, "  size   = %d\n", vring->size);  	seq_printf(s, "  swtail = %d\n", vring->swtail); @@ -50,8 +52,8 @@ static void wil_print_vring(struct seq_file *s, struct wil6210_priv *wil,  			volatile struct vring_tx_desc *d = &vring->va[i].tx;  			if ((i % 64) == 0 && (i != 0))  				seq_printf(s, "\n"); -			seq_printf(s, "%s", (d->dma.status & BIT(0)) ? -					"S" : (vring->ctx[i].skb ? "H" : "h")); +			seq_printf(s, "%c", (d->dma.status & BIT(0)) ? +					_s : (vring->ctx[i].skb ? _h : 'h'));  		}  		seq_printf(s, "\n");  	} @@ -63,14 +65,19 @@ static int wil_vring_debugfs_show(struct seq_file *s, void *data)  	uint i;  	struct wil6210_priv *wil = s->private; -	wil_print_vring(s, wil, "rx", &wil->vring_rx); +	wil_print_vring(s, wil, "rx", &wil->vring_rx, 'S', '_');  	for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) {  		struct vring *vring = &(wil->vring_tx[i]);  		if (vring->va) { +			int cid = wil->vring2cid_tid[i][0]; +			int tid = wil->vring2cid_tid[i][1];  			char name[10];  			snprintf(name, sizeof(name), "tx_%2d", i); -			wil_print_vring(s, wil, name, vring); + +			seq_printf(s, "\n%pM CID %d TID %d\n", +				   wil->sta[cid].addr, cid, tid); +			wil_print_vring(s, wil, name, vring, '_', 'H');  		}  	} @@ -390,57 +397,98 @@ static const struct file_operations fops_reset = {  	.write = wil_write_file_reset,  	.open  = simple_open,  }; -/*---------Tx descriptor------------*/ +static void wil_seq_hexdump(struct seq_file *s, void *p, int len, +			    const char *prefix) +{ +	char printbuf[16 * 3 + 2]; +	int i = 0; +	while (i < len) { +		int l = min(len - i, 16); +		hex_dump_to_buffer(p + i, l, 16, 1, printbuf, +				   sizeof(printbuf), false); +		seq_printf(s, "%s%s\n", prefix, printbuf); +		i += l; +	} +} + +static void wil_seq_print_skb(struct seq_file *s, struct sk_buff *skb) +{ +	int i = 0; +	int len = skb_headlen(skb); +	void *p = skb->data; +	int nr_frags = skb_shinfo(skb)->nr_frags; + +	seq_printf(s, "    len = %d\n", len); +	wil_seq_hexdump(s, p, len, "      : "); + +	if (nr_frags) { +		seq_printf(s, "    nr_frags = %d\n", nr_frags); +		for (i = 0; i < nr_frags; i++) { +			const struct skb_frag_struct *frag = +					&skb_shinfo(skb)->frags[i]; + +			len = skb_frag_size(frag); +			p = skb_frag_address_safe(frag); +			seq_printf(s, "    [%2d] : len = %d\n", i, len); +			wil_seq_hexdump(s, p, len, "      : "); +		} +	} +} + +/*---------Tx/Rx descriptor------------*/  static int wil_txdesc_debugfs_show(struct seq_file *s, void *data)  {  	struct wil6210_priv *wil = s->private; -	struct vring *vring = &(wil->vring_tx[0]); +	struct vring *vring; +	bool tx = (dbg_vring_index < WIL6210_MAX_TX_RINGS); +	if (tx) +		vring = &(wil->vring_tx[dbg_vring_index]); +	else +		vring = &wil->vring_rx;  	if (!vring->va) { -		seq_printf(s, "No Tx VRING\n"); +		if (tx) +			seq_printf(s, "No Tx[%2d] VRING\n", dbg_vring_index); +		else +			seq_puts(s, "No Rx VRING\n");  		return 0;  	}  	if (dbg_txdesc_index < vring->size) { +		/* use struct vring_tx_desc for Rx as well, +		 * only field used, .dma.length, is the same +		 */  		volatile struct vring_tx_desc *d =  				&(vring->va[dbg_txdesc_index].tx);  		volatile u32 *u = (volatile u32 *)d;  		struct sk_buff *skb = vring->ctx[dbg_txdesc_index].skb; -		seq_printf(s, "Tx[%3d] = {\n", dbg_txdesc_index); +		if (tx) +			seq_printf(s, "Tx[%2d][%3d] = {\n", dbg_vring_index, +				   dbg_txdesc_index); +		else +			seq_printf(s, "Rx[%3d] = {\n", dbg_txdesc_index);  		seq_printf(s, "  MAC = 0x%08x 0x%08x 0x%08x 0x%08x\n",  			   u[0], u[1], u[2], u[3]);  		seq_printf(s, "  DMA = 0x%08x 0x%08x 0x%08x 0x%08x\n",  			   u[4], u[5], u[6], u[7]); -		seq_printf(s, "  SKB = %p\n", skb); +		seq_printf(s, "  SKB = 0x%p\n", skb);  		if (skb) { -			char printbuf[16 * 3 + 2]; -			int i = 0; -			int len = le16_to_cpu(d->dma.length); -			void *p = skb->data; - -			if (len != skb_headlen(skb)) { -				seq_printf(s, "!!! len: desc = %d skb = %d\n", -					   len, skb_headlen(skb)); -				len = min_t(int, len, skb_headlen(skb)); -			} - -			seq_printf(s, "    len = %d\n", len); - -			while (i < len) { -				int l = min(len - i, 16); -				hex_dump_to_buffer(p + i, l, 16, 1, printbuf, -						   sizeof(printbuf), false); -				seq_printf(s, "      : %s\n", printbuf); -				i += l; -			} +			skb_get(skb); +			wil_seq_print_skb(s, skb); +			kfree_skb(skb);  		}  		seq_printf(s, "}\n");  	} else { -		seq_printf(s, "TxDesc index (%d) >= size (%d)\n", -			   dbg_txdesc_index, vring->size); +		if (tx) +			seq_printf(s, "[%2d] TxDesc index (%d) >= size (%d)\n", +				   dbg_vring_index, dbg_txdesc_index, +				   vring->size); +		else +			seq_printf(s, "RxDesc index (%d) >= size (%d)\n", +				   dbg_txdesc_index, vring->size);  	}  	return 0; @@ -570,6 +618,69 @@ static const struct file_operations fops_temp = {  	.llseek		= seq_lseek,  }; +/*---------Station matrix------------*/ +static void wil_print_rxtid(struct seq_file *s, struct wil_tid_ampdu_rx *r) +{ +	int i; +	u16 index = ((r->head_seq_num - r->ssn) & 0xfff) % r->buf_size; +	seq_printf(s, "0x%03x [", r->head_seq_num); +	for (i = 0; i < r->buf_size; i++) { +		if (i == index) +			seq_printf(s, "%c", r->reorder_buf[i] ? 'O' : '|'); +		else +			seq_printf(s, "%c", r->reorder_buf[i] ? '*' : '_'); +	} +	seq_puts(s, "]\n"); +} + +static int wil_sta_debugfs_show(struct seq_file *s, void *data) +{ +	struct wil6210_priv *wil = s->private; +	int i, tid; + +	for (i = 0; i < ARRAY_SIZE(wil->sta); i++) { +		struct wil_sta_info *p = &wil->sta[i]; +		char *status = "unknown"; +		switch (p->status) { +		case wil_sta_unused: +			status = "unused   "; +			break; +		case wil_sta_conn_pending: +			status = "pending  "; +			break; +		case wil_sta_connected: +			status = "connected"; +			break; +		} +		seq_printf(s, "[%d] %pM %s%s\n", i, p->addr, status, +			   (p->data_port_open ? " data_port_open" : "")); + +		if (p->status == wil_sta_connected) { +			for (tid = 0; tid < WIL_STA_TID_NUM; tid++) { +				struct wil_tid_ampdu_rx *r = p->tid_rx[tid]; +				if (r) { +					seq_printf(s, "[%2d] ", tid); +					wil_print_rxtid(s, r); +				} +			} +		} +	} + +	return 0; +} + +static int wil_sta_seq_open(struct inode *inode, struct file *file) +{ +	return single_open(file, wil_sta_debugfs_show, inode->i_private); +} + +static const struct file_operations fops_sta = { +	.open		= wil_sta_seq_open, +	.release	= single_release, +	.read		= seq_read, +	.llseek		= seq_lseek, +}; +  /*----------------*/  int wil6210_debugfs_init(struct wil6210_priv *wil)  { @@ -581,9 +692,13 @@ int wil6210_debugfs_init(struct wil6210_priv *wil)  	debugfs_create_file("mbox", S_IRUGO, dbg, wil, &fops_mbox);  	debugfs_create_file("vrings", S_IRUGO, dbg, wil, &fops_vring); -	debugfs_create_file("txdesc", S_IRUGO, dbg, wil, &fops_txdesc); -	debugfs_create_u32("txdesc_index", S_IRUGO | S_IWUSR, dbg, +	debugfs_create_file("stations", S_IRUGO, dbg, wil, &fops_sta); +	debugfs_create_file("desc", S_IRUGO, dbg, wil, &fops_txdesc); +	debugfs_create_u32("desc_index", S_IRUGO | S_IWUSR, dbg,  			   &dbg_txdesc_index); +	debugfs_create_u32("vring_index", S_IRUGO | S_IWUSR, dbg, +			   &dbg_vring_index); +  	debugfs_create_file("bf", S_IRUGO, dbg, wil, &fops_bf);  	debugfs_create_file("ssid", S_IRUGO | S_IWUSR, dbg, wil, &fops_ssid);  	debugfs_create_u32("secure_pcp", S_IRUGO | S_IWUSR, dbg, diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c index 8205d3e4ab6..73593aa3cd9 100644 --- a/drivers/net/wireless/ath/wil6210/interrupt.c +++ b/drivers/net/wireless/ath/wil6210/interrupt.c @@ -156,6 +156,19 @@ void wil6210_enable_irq(struct wil6210_priv *wil)  	iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_MISC_ICR) +  		  offsetof(struct RGF_ICR, ICC)); +	/* interrupt moderation parameters */ +	if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) { +		/* disable interrupt moderation for monitor +		 * to get better timestamp precision +		 */ +		iowrite32(0, wil->csr + HOSTADDR(RGF_DMA_ITR_CNT_CRL)); +	} else { +		iowrite32(WIL6210_ITR_TRSH, +			  wil->csr + HOSTADDR(RGF_DMA_ITR_CNT_TRSH)); +		iowrite32(BIT_DMA_ITR_CNT_CRL_EN, +			  wil->csr + HOSTADDR(RGF_DMA_ITR_CNT_CRL)); +	} +  	wil6210_unmask_irq_pseudo(wil);  	wil6210_unmask_irq_tx(wil);  	wil6210_unmask_irq_rx(wil); @@ -182,8 +195,12 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)  	if (isr & BIT_DMA_EP_RX_ICR_RX_DONE) {  		wil_dbg_irq(wil, "RX done\n");  		isr &= ~BIT_DMA_EP_RX_ICR_RX_DONE; -		wil_dbg_txrx(wil, "NAPI schedule\n"); -		napi_schedule(&wil->napi_rx); +		if (test_bit(wil_status_reset_done, &wil->status)) { +			wil_dbg_txrx(wil, "NAPI(Rx) schedule\n"); +			napi_schedule(&wil->napi_rx); +		} else { +			wil_err(wil, "Got Rx interrupt while in reset\n"); +		}  	}  	if (isr) @@ -213,10 +230,15 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie)  	if (isr & BIT_DMA_EP_TX_ICR_TX_DONE) {  		wil_dbg_irq(wil, "TX done\n"); -		napi_schedule(&wil->napi_tx);  		isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE;  		/* clear also all VRING interrupts */  		isr &= ~(BIT(25) - 1UL); +		if (test_bit(wil_status_reset_done, &wil->status)) { +			wil_dbg_txrx(wil, "NAPI(Tx) schedule\n"); +			napi_schedule(&wil->napi_tx); +		} else { +			wil_err(wil, "Got Tx interrupt while in reset\n"); +		}  	}  	if (isr) @@ -306,6 +328,7 @@ static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie)  	if (isr & ISR_MISC_FW_ERROR) {  		wil_notify_fw_error(wil);  		isr &= ~ISR_MISC_FW_ERROR; +		wil_fw_error_recovery(wil);  	}  	if (isr & ISR_MISC_MBOX_EVT) { @@ -315,7 +338,7 @@ static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie)  	}  	if (isr) -		wil_err(wil, "un-handled MISC ISR bits 0x%08x\n", isr); +		wil_dbg_irq(wil, "un-handled MISC ISR bits 0x%08x\n", isr);  	wil->isr_misc = 0; @@ -480,6 +503,23 @@ free0:  	return rc;  } +/* can't use wil_ioread32_and_clear because ICC value is not ser yet */ +static inline void wil_clear32(void __iomem *addr) +{ +	u32 x = ioread32(addr); + +	iowrite32(x, addr); +} + +void wil6210_clear_irq(struct wil6210_priv *wil) +{ +	wil_clear32(wil->csr + HOSTADDR(RGF_DMA_EP_RX_ICR) + +		    offsetof(struct RGF_ICR, ICR)); +	wil_clear32(wil->csr + HOSTADDR(RGF_DMA_EP_TX_ICR) + +		    offsetof(struct RGF_ICR, ICR)); +	wil_clear32(wil->csr + HOSTADDR(RGF_DMA_EP_MISC_ICR) + +		    offsetof(struct RGF_ICR, ICR)); +}  int wil6210_init_irq(struct wil6210_priv *wil, int irq)  { diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 0a2844c48a6..11e6d9d22ea 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -16,8 +16,14 @@  #include <linux/moduleparam.h>  #include <linux/if_arp.h> +#include <linux/etherdevice.h>  #include "wil6210.h" +#include "txrx.h" + +static bool no_fw_recovery; +module_param(no_fw_recovery, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(no_fw_recovery, " disable FW error recovery");  /*   * Due to a hardware issue, @@ -52,29 +58,74 @@ void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src,  		__raw_writel(*s++, d++);  } -static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid) +static void wil_disconnect_cid(struct wil6210_priv *wil, int cid)  {  	uint i; -	struct net_device *ndev = wil_to_ndev(wil); +	struct wil_sta_info *sta = &wil->sta[cid]; -	wil_dbg_misc(wil, "%s()\n", __func__); +	sta->data_port_open = false; +	if (sta->status != wil_sta_unused) { +		wmi_disconnect_sta(wil, sta->addr, WLAN_REASON_DEAUTH_LEAVING); +		sta->status = wil_sta_unused; +	} -	wil_link_off(wil); -	if (test_bit(wil_status_fwconnected, &wil->status)) { -		clear_bit(wil_status_fwconnected, &wil->status); -		cfg80211_disconnected(ndev, -				      WLAN_STATUS_UNSPECIFIED_FAILURE, -				      NULL, 0, GFP_KERNEL); -	} else if (test_bit(wil_status_fwconnecting, &wil->status)) { -		cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0, -					WLAN_STATUS_UNSPECIFIED_FAILURE, -					GFP_KERNEL); +	for (i = 0; i < WIL_STA_TID_NUM; i++) { +		struct wil_tid_ampdu_rx *r = sta->tid_rx[i]; +		sta->tid_rx[i] = NULL; +		wil_tid_ampdu_rx_free(wil, r); +	} +	for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) { +		if (wil->vring2cid_tid[i][0] == cid) +			wil_vring_fini_tx(wil, i);  	} -	clear_bit(wil_status_fwconnecting, &wil->status); -	for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) -		wil_vring_fini_tx(wil, i); +	memset(&sta->stats, 0, sizeof(sta->stats)); +} -	clear_bit(wil_status_dontscan, &wil->status); +static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid) +{ +	int cid = -ENOENT; +	struct net_device *ndev = wil_to_ndev(wil); +	struct wireless_dev *wdev = wil->wdev; + +	might_sleep(); +	if (bssid) { +		cid = wil_find_cid(wil, bssid); +		wil_dbg_misc(wil, "%s(%pM, CID %d)\n", __func__, bssid, cid); +	} else { +		wil_dbg_misc(wil, "%s(all)\n", __func__); +	} + +	if (cid >= 0) /* disconnect 1 peer */ +		wil_disconnect_cid(wil, cid); +	else /* disconnect all */ +		for (cid = 0; cid < WIL6210_MAX_CID; cid++) +			wil_disconnect_cid(wil, cid); + +	/* link state */ +	switch (wdev->iftype) { +	case NL80211_IFTYPE_STATION: +	case NL80211_IFTYPE_P2P_CLIENT: +		wil_link_off(wil); +		if (test_bit(wil_status_fwconnected, &wil->status)) { +			clear_bit(wil_status_fwconnected, &wil->status); +			cfg80211_disconnected(ndev, +					      WLAN_STATUS_UNSPECIFIED_FAILURE, +					      NULL, 0, GFP_KERNEL); +		} else if (test_bit(wil_status_fwconnecting, &wil->status)) { +			cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0, +						WLAN_STATUS_UNSPECIFIED_FAILURE, +						GFP_KERNEL); +		} +		clear_bit(wil_status_fwconnecting, &wil->status); +		break; +	default: +		/* AP-like interface and monitor: +		 * never scan, always connected +		 */ +		if (bssid) +			cfg80211_del_sta(ndev, bssid, GFP_KERNEL); +		break; +	}  }  static void wil_disconnect_worker(struct work_struct *work) @@ -82,7 +133,9 @@ static void wil_disconnect_worker(struct work_struct *work)  	struct wil6210_priv *wil = container_of(work,  			struct wil6210_priv, disconnect_worker); +	mutex_lock(&wil->mutex);  	_wil6210_disconnect(wil, NULL); +	mutex_unlock(&wil->mutex);  }  static void wil_connect_timer_fn(ulong x) @@ -97,12 +150,82 @@ static void wil_connect_timer_fn(ulong x)  	schedule_work(&wil->disconnect_worker);  } +static void wil_scan_timer_fn(ulong x) +{ +	struct wil6210_priv *wil = (void *)x; + +	clear_bit(wil_status_fwready, &wil->status); +	wil_err(wil, "Scan timeout detected, start fw error recovery\n"); +	schedule_work(&wil->fw_error_worker); +} + +static void wil_fw_error_worker(struct work_struct *work) +{ +	struct wil6210_priv *wil = container_of(work, +			struct wil6210_priv, fw_error_worker); +	struct wireless_dev *wdev = wil->wdev; + +	wil_dbg_misc(wil, "fw error worker\n"); + +	if (no_fw_recovery) +		return; + +	/* increment @recovery_count if less then WIL6210_FW_RECOVERY_TO +	 * passed since last recovery attempt +	 */ +	if (time_is_after_jiffies(wil->last_fw_recovery + +				  WIL6210_FW_RECOVERY_TO)) +		wil->recovery_count++; +	else +		wil->recovery_count = 1; /* fw was alive for a long time */ + +	if (wil->recovery_count > WIL6210_FW_RECOVERY_RETRIES) { +		wil_err(wil, "too many recovery attempts (%d), giving up\n", +			wil->recovery_count); +		return; +	} + +	wil->last_fw_recovery = jiffies; + +	mutex_lock(&wil->mutex); +	switch (wdev->iftype) { +	case NL80211_IFTYPE_STATION: +	case NL80211_IFTYPE_P2P_CLIENT: +	case NL80211_IFTYPE_MONITOR: +		wil_info(wil, "fw error recovery started (try %d)...\n", +			 wil->recovery_count); +		wil_reset(wil); + +		/* need to re-allocate Rx ring after reset */ +		wil_rx_init(wil); +		break; +	case NL80211_IFTYPE_AP: +	case NL80211_IFTYPE_P2P_GO: +		/* recovery in these modes is done by upper layers */ +		break; +	default: +		break; +	} +	mutex_unlock(&wil->mutex); +} + +static int wil_find_free_vring(struct wil6210_priv *wil) +{ +	int i; +	for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) { +		if (!wil->vring_tx[i].va) +			return i; +	} +	return -EINVAL; +} +  static void wil_connect_worker(struct work_struct *work)  {  	int rc;  	struct wil6210_priv *wil = container_of(work, struct wil6210_priv,  						connect_worker);  	int cid = wil->pending_connect_cid; +	int ringid = wil_find_free_vring(wil);  	if (cid < 0) {  		wil_err(wil, "No connection pending\n"); @@ -111,16 +234,22 @@ static void wil_connect_worker(struct work_struct *work)  	wil_dbg_wmi(wil, "Configure for connection CID %d\n", cid); -	rc = wil_vring_init_tx(wil, 0, WIL6210_TX_RING_SIZE, cid, 0); +	rc = wil_vring_init_tx(wil, ringid, WIL6210_TX_RING_SIZE, cid, 0);  	wil->pending_connect_cid = -1; -	if (rc == 0) +	if (rc == 0) { +		wil->sta[cid].status = wil_sta_connected;  		wil_link_on(wil); +	} else { +		wil->sta[cid].status = wil_sta_unused; +	}  }  int wil_priv_init(struct wil6210_priv *wil)  {  	wil_dbg_misc(wil, "%s()\n", __func__); +	memset(wil->sta, 0, sizeof(wil->sta)); +  	mutex_init(&wil->mutex);  	mutex_init(&wil->wmi_mutex); @@ -128,10 +257,12 @@ int wil_priv_init(struct wil6210_priv *wil)  	wil->pending_connect_cid = -1;  	setup_timer(&wil->connect_timer, wil_connect_timer_fn, (ulong)wil); +	setup_timer(&wil->scan_timer, wil_scan_timer_fn, (ulong)wil);  	INIT_WORK(&wil->connect_worker, wil_connect_worker);  	INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker);  	INIT_WORK(&wil->wmi_event_worker, wmi_event_worker); +	INIT_WORK(&wil->fw_error_worker, wil_fw_error_worker);  	INIT_LIST_HEAD(&wil->pending_wmi_ev);  	spin_lock_init(&wil->wmi_ev_lock); @@ -146,10 +277,12 @@ int wil_priv_init(struct wil6210_priv *wil)  		return -EAGAIN;  	} +	wil->last_fw_recovery = jiffies; +  	return 0;  } -void wil6210_disconnect(struct wil6210_priv *wil, void *bssid) +void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid)  {  	del_timer_sync(&wil->connect_timer);  	_wil6210_disconnect(wil, bssid); @@ -157,8 +290,12 @@ void wil6210_disconnect(struct wil6210_priv *wil, void *bssid)  void wil_priv_deinit(struct wil6210_priv *wil)  { +	del_timer_sync(&wil->scan_timer);  	cancel_work_sync(&wil->disconnect_worker); +	cancel_work_sync(&wil->fw_error_worker); +	mutex_lock(&wil->mutex);  	wil6210_disconnect(wil, NULL); +	mutex_unlock(&wil->mutex);  	wmi_event_flush(wil);  	destroy_workqueue(wil->wmi_wq_conn);  	destroy_workqueue(wil->wmi_wq); @@ -166,40 +303,78 @@ void wil_priv_deinit(struct wil6210_priv *wil)  static void wil_target_reset(struct wil6210_priv *wil)  { +	int delay = 0; +	u32 hw_state; +	u32 rev_id; +  	wil_dbg_misc(wil, "Resetting...\n"); +	/* register read */ +#define R(a) ioread32(wil->csr + HOSTADDR(a))  	/* register write */  #define W(a, v) iowrite32(v, wil->csr + HOSTADDR(a))  	/* register set = read, OR, write */ -#define S(a, v) iowrite32(ioread32(wil->csr + HOSTADDR(a)) | v, \ -		wil->csr + HOSTADDR(a)) +#define S(a, v) W(a, R(a) | v) +	/* register clear = read, AND with inverted, write */ +#define C(a, v) W(a, R(a) & ~v) +	wil->hw_version = R(RGF_USER_FW_REV_ID); +	rev_id = wil->hw_version & 0xff;  	/* hpal_perst_from_pad_src_n_mask */  	S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(6));  	/* car_perst_rst_src_n_mask */  	S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(7)); +	wmb(); /* order is important here */  	W(RGF_USER_MAC_CPU_0,  BIT(1)); /* mac_cpu_man_rst */  	W(RGF_USER_USER_CPU_0, BIT(1)); /* user_cpu_man_rst */ +	wmb(); /* order is important here */  	W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0xFE000000);  	W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0x0000003F);  	W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000170);  	W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xFFE7FC00); +	wmb(); /* order is important here */  	W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0);  	W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0);  	W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0);  	W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); +	wmb(); /* order is important here */  	W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000001); -	W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00000080); +	if (rev_id == 1) { +		W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00000080); +	} else { +		W(RGF_PCIE_LOS_COUNTER_CTL, BIT(6) | BIT(8)); +		W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00008000); +	}  	W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); +	wmb(); /* order is important here */ -	wil_dbg_misc(wil, "Reset completed\n"); +	/* wait until device ready */ +	do { +		msleep(1); +		hw_state = R(RGF_USER_HW_MACHINE_STATE); +		if (delay++ > 100) { +			wil_err(wil, "Reset not completed, hw_state 0x%08x\n", +				hw_state); +			return; +		} +	} while (hw_state != HW_MACHINE_BOOT_DONE); +	if (rev_id == 2) +		W(RGF_PCIE_LOS_COUNTER_CTL, BIT(8)); + +	C(RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD); +	wmb(); /* order is important here */ + +	wil_dbg_misc(wil, "Reset completed in %d ms\n", delay); + +#undef R  #undef W  #undef S +#undef C  }  void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r) @@ -219,8 +394,8 @@ static int wil_wait_for_fw_ready(struct wil6210_priv *wil)  		wil_err(wil, "Firmware not ready\n");  		return -ETIME;  	} else { -		wil_dbg_misc(wil, "FW ready after %d ms\n", -			     jiffies_to_msecs(to-left)); +		wil_info(wil, "FW ready after %d ms. HW version 0x%08x\n", +			 jiffies_to_msecs(to-left), wil->hw_version);  	}  	return 0;  } @@ -234,11 +409,25 @@ int wil_reset(struct wil6210_priv *wil)  {  	int rc; +	WARN_ON(!mutex_is_locked(&wil->mutex)); +  	cancel_work_sync(&wil->disconnect_worker);  	wil6210_disconnect(wil, NULL); +	wil->status = 0; /* prevent NAPI from being scheduled */ +	if (test_bit(wil_status_napi_en, &wil->status)) { +		napi_synchronize(&wil->napi_rx); +	} + +	if (wil->scan_request) { +		wil_dbg_misc(wil, "Abort scan_request 0x%p\n", +			     wil->scan_request); +		del_timer_sync(&wil->scan_timer); +		cfg80211_scan_done(wil->scan_request, true); +		wil->scan_request = NULL; +	} +  	wil6210_disable_irq(wil); -	wil->status = 0;  	wmi_event_flush(wil); @@ -248,9 +437,11 @@ int wil_reset(struct wil6210_priv *wil)  	/* TODO: put MAC in reset */  	wil_target_reset(wil); +	wil_rx_fini(wil); +  	/* init after reset */  	wil->pending_connect_cid = -1; -	INIT_COMPLETION(wil->wmi_ready); +	reinit_completion(&wil->wmi_ready);  	/* TODO: release MAC reset */  	wil6210_enable_irq(wil); @@ -261,6 +452,11 @@ int wil_reset(struct wil6210_priv *wil)  	return rc;  } +void wil_fw_error_recovery(struct wil6210_priv *wil) +{ +	wil_dbg_misc(wil, "starting fw error recovery\n"); +	schedule_work(&wil->fw_error_worker); +}  void wil_link_on(struct wil6210_priv *wil)  { @@ -288,6 +484,8 @@ static int __wil_up(struct wil6210_priv *wil)  	struct wireless_dev *wdev = wil->wdev;  	int rc; +	WARN_ON(!mutex_is_locked(&wil->mutex)); +  	rc = wil_reset(wil);  	if (rc)  		return rc; @@ -329,6 +527,7 @@ static int __wil_up(struct wil6210_priv *wil)  	napi_enable(&wil->napi_rx);  	napi_enable(&wil->napi_tx); +	set_bit(wil_status_napi_en, &wil->status);  	return 0;  } @@ -346,10 +545,14 @@ int wil_up(struct wil6210_priv *wil)  static int __wil_down(struct wil6210_priv *wil)  { +	WARN_ON(!mutex_is_locked(&wil->mutex)); + +	clear_bit(wil_status_napi_en, &wil->status);  	napi_disable(&wil->napi_rx);  	napi_disable(&wil->napi_tx);  	if (wil->scan_request) { +		del_timer_sync(&wil->scan_timer);  		cfg80211_scan_done(wil->scan_request, true);  		wil->scan_request = NULL;  	} @@ -370,3 +573,19 @@ int wil_down(struct wil6210_priv *wil)  	return rc;  } + +int wil_find_cid(struct wil6210_priv *wil, const u8 *mac) +{ +	int i; +	int rc = -ENOENT; + +	for (i = 0; i < ARRAY_SIZE(wil->sta); i++) { +		if ((wil->sta[i].status != wil_sta_unused) && +		    ether_addr_equal(wil->sta[i].addr, mac)) { +			rc = i; +			break; +		} +	} + +	return rc; +} diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c index 717178f09aa..106b6dcb773 100644 --- a/drivers/net/wireless/ath/wil6210/netdev.c +++ b/drivers/net/wireless/ath/wil6210/netdev.c @@ -32,12 +32,26 @@ static int wil_stop(struct net_device *ndev)  	return wil_down(wil);  } +static int wil_change_mtu(struct net_device *ndev, int new_mtu) +{ +	struct wil6210_priv *wil = ndev_to_wil(ndev); + +	if (new_mtu < 68 || new_mtu > IEEE80211_MAX_DATA_LEN_DMG) +		return -EINVAL; + +	wil_dbg_misc(wil, "change MTU %d -> %d\n", ndev->mtu, new_mtu); +	ndev->mtu = new_mtu; + +	return 0; +} +  static const struct net_device_ops wil_netdev_ops = {  	.ndo_open		= wil_open,  	.ndo_stop		= wil_stop,  	.ndo_start_xmit		= wil_start_xmit,  	.ndo_set_mac_address	= eth_mac_addr,  	.ndo_validate_addr	= eth_validate_addr, +	.ndo_change_mtu		= wil_change_mtu,  };  static int wil6210_netdev_poll_rx(struct napi_struct *napi, int budget) @@ -127,8 +141,9 @@ void *wil_if_alloc(struct device *dev, void __iomem *csr)  	ndev->netdev_ops = &wil_netdev_ops;  	ndev->ieee80211_ptr = wdev; -	ndev->hw_features = NETIF_F_HW_CSUM | NETIF_F_RXCSUM; -	ndev->features |= NETIF_F_HW_CSUM | NETIF_F_RXCSUM; +	ndev->hw_features = NETIF_F_HW_CSUM | NETIF_F_RXCSUM | +			    NETIF_F_SG | NETIF_F_GRO; +	ndev->features |= ndev->hw_features;  	SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));  	wdev->netdev = ndev; diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c index eb1dc7ad80f..1e2e07b9d13 100644 --- a/drivers/net/wireless/ath/wil6210/pcie_bus.c +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -41,36 +41,36 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil)  	switch (use_msi) {  	case 3:  	case 1: +		wil_dbg_misc(wil, "Setup %d MSI interrupts\n", use_msi); +		break;  	case 0: +		wil_dbg_misc(wil, "MSI interrupts disabled, use INTx\n");  		break;  	default: -		wil_err(wil, "Invalid use_msi=%d, default to 1\n", -			use_msi); +		wil_err(wil, "Invalid use_msi=%d, default to 1\n", use_msi);  		use_msi = 1;  	} -	wil->n_msi = use_msi; -	if (wil->n_msi) { -		wil_dbg_misc(wil, "Setup %d MSI interrupts\n", use_msi); -		rc = pci_enable_msi_block(pdev, wil->n_msi); -		if (rc && (wil->n_msi == 3)) { -			wil_err(wil, "3 MSI mode failed, try 1 MSI\n"); -			wil->n_msi = 1; -			rc = pci_enable_msi_block(pdev, wil->n_msi); -		} -		if (rc) { -			wil_err(wil, "pci_enable_msi failed, use INTx\n"); -			wil->n_msi = 0; -		} -	} else { -		wil_dbg_misc(wil, "MSI interrupts disabled, use INTx\n"); + +	if (use_msi == 3 && pci_enable_msi_range(pdev, 3, 3) < 0) { +		wil_err(wil, "3 MSI mode failed, try 1 MSI\n"); +		use_msi = 1; +	} + +	if (use_msi == 1 && pci_enable_msi(pdev)) { +		wil_err(wil, "pci_enable_msi failed, use INTx\n"); +		use_msi = 0;  	} +	wil->n_msi = use_msi; +  	rc = wil6210_init_irq(wil, pdev->irq);  	if (rc)  		goto stop_master;  	/* need reset here to obtain MAC */ +	mutex_lock(&wil->mutex);  	rc = wil_reset(wil); +	mutex_unlock(&wil->mutex);  	if (rc)  		goto release_irq; @@ -138,7 +138,7 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)  		goto err_release_reg;  	}  	/* rollback to err_iounmap */ -	dev_info(&pdev->dev, "CSR at %pR -> %p\n", &pdev->resource[0], csr); +	dev_info(&pdev->dev, "CSR at %pR -> 0x%p\n", &pdev->resource[0], csr);  	wil = wil_if_alloc(dev, csr);  	if (IS_ERR(wil)) { @@ -151,6 +151,7 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)  	pci_set_drvdata(pdev, wil);  	wil->pdev = pdev; +	wil6210_clear_irq(wil);  	/* FW should raise IRQ when ready */  	rc = wil_if_pcie_enable(wil);  	if (rc) { @@ -197,7 +198,6 @@ static void wil_pcie_remove(struct pci_dev *pdev)  	pci_iounmap(pdev, wil->csr);  	pci_release_region(pdev, 0);  	pci_disable_device(pdev); -	pci_set_drvdata(pdev, NULL);  }  static DEFINE_PCI_DEVICE_TABLE(wil6210_pcie_ids) = { diff --git a/drivers/net/wireless/ath/wil6210/rx_reorder.c b/drivers/net/wireless/ath/wil6210/rx_reorder.c new file mode 100644 index 00000000000..747ae127587 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/rx_reorder.c @@ -0,0 +1,201 @@ +#include "wil6210.h" +#include "txrx.h" + +#define SEQ_MODULO 0x1000 +#define SEQ_MASK   0xfff + +static inline int seq_less(u16 sq1, u16 sq2) +{ +	return ((sq1 - sq2) & SEQ_MASK) > (SEQ_MODULO >> 1); +} + +static inline u16 seq_inc(u16 sq) +{ +	return (sq + 1) & SEQ_MASK; +} + +static inline u16 seq_sub(u16 sq1, u16 sq2) +{ +	return (sq1 - sq2) & SEQ_MASK; +} + +static inline int reorder_index(struct wil_tid_ampdu_rx *r, u16 seq) +{ +	return seq_sub(seq, r->ssn) % r->buf_size; +} + +static void wil_release_reorder_frame(struct wil6210_priv *wil, +				      struct wil_tid_ampdu_rx *r, +				      int index) +{ +	struct net_device *ndev = wil_to_ndev(wil); +	struct sk_buff *skb = r->reorder_buf[index]; + +	if (!skb) +		goto no_frame; + +	/* release the frame from the reorder ring buffer */ +	r->stored_mpdu_num--; +	r->reorder_buf[index] = NULL; +	wil_netif_rx_any(skb, ndev); + +no_frame: +	r->head_seq_num = seq_inc(r->head_seq_num); +} + +static void wil_release_reorder_frames(struct wil6210_priv *wil, +				       struct wil_tid_ampdu_rx *r, +				       u16 hseq) +{ +	int index; + +	/* note: this function is never called with +	 * hseq preceding r->head_seq_num, i.e it is always true +	 * !seq_less(hseq, r->head_seq_num) +	 * and thus on loop exit it should be +	 * r->head_seq_num == hseq +	 */ +	while (seq_less(r->head_seq_num, hseq) && r->stored_mpdu_num) { +		index = reorder_index(r, r->head_seq_num); +		wil_release_reorder_frame(wil, r, index); +	} +	r->head_seq_num = hseq; +} + +static void wil_reorder_release(struct wil6210_priv *wil, +				struct wil_tid_ampdu_rx *r) +{ +	int index = reorder_index(r, r->head_seq_num); + +	while (r->reorder_buf[index]) { +		wil_release_reorder_frame(wil, r, index); +		index = reorder_index(r, r->head_seq_num); +	} +} + +void wil_rx_reorder(struct wil6210_priv *wil, struct sk_buff *skb) +{ +	struct net_device *ndev = wil_to_ndev(wil); +	struct vring_rx_desc *d = wil_skb_rxdesc(skb); +	int tid = wil_rxdesc_tid(d); +	int cid = wil_rxdesc_cid(d); +	int mid = wil_rxdesc_mid(d); +	u16 seq = wil_rxdesc_seq(d); +	struct wil_sta_info *sta = &wil->sta[cid]; +	struct wil_tid_ampdu_rx *r = sta->tid_rx[tid]; +	u16 hseq; +	int index; + +	wil_dbg_txrx(wil, "MID %d CID %d TID %d Seq 0x%03x\n", +		     mid, cid, tid, seq); + +	if (!r) { +		wil_netif_rx_any(skb, ndev); +		return; +	} + +	hseq = r->head_seq_num; + +	spin_lock(&r->reorder_lock); + +	/** Due to the race between WMI events, where BACK establishment +	 * reported, and data Rx, few packets may be pass up before reorder +	 * buffer get allocated. Catch up by pretending SSN is what we +	 * see in the 1-st Rx packet +	 */ +	if (r->first_time) { +		r->first_time = false; +		if (seq != r->head_seq_num) { +			wil_err(wil, "Error: 1-st frame with wrong sequence" +				" %d, should be %d. Fixing...\n", seq, +				r->head_seq_num); +			r->head_seq_num = seq; +			r->ssn = seq; +		} +	} + +	/* frame with out of date sequence number */ +	if (seq_less(seq, r->head_seq_num)) { +		dev_kfree_skb(skb); +		goto out; +	} + +	/* +	 * If frame the sequence number exceeds our buffering window +	 * size release some previous frames to make room for this one. +	 */ +	if (!seq_less(seq, r->head_seq_num + r->buf_size)) { +		hseq = seq_inc(seq_sub(seq, r->buf_size)); +		/* release stored frames up to new head to stack */ +		wil_release_reorder_frames(wil, r, hseq); +	} + +	/* Now the new frame is always in the range of the reordering buffer */ + +	index = reorder_index(r, seq); + +	/* check if we already stored this frame */ +	if (r->reorder_buf[index]) { +		dev_kfree_skb(skb); +		goto out; +	} + +	/* +	 * If the current MPDU is in the right order and nothing else +	 * is stored we can process it directly, no need to buffer it. +	 * If it is first but there's something stored, we may be able +	 * to release frames after this one. +	 */ +	if (seq == r->head_seq_num && r->stored_mpdu_num == 0) { +		r->head_seq_num = seq_inc(r->head_seq_num); +		wil_netif_rx_any(skb, ndev); +		goto out; +	} + +	/* put the frame in the reordering buffer */ +	r->reorder_buf[index] = skb; +	r->reorder_time[index] = jiffies; +	r->stored_mpdu_num++; +	wil_reorder_release(wil, r); + +out: +	spin_unlock(&r->reorder_lock); +} + +struct wil_tid_ampdu_rx *wil_tid_ampdu_rx_alloc(struct wil6210_priv *wil, +						int size, u16 ssn) +{ +	struct wil_tid_ampdu_rx *r = kzalloc(sizeof(*r), GFP_KERNEL); +	if (!r) +		return NULL; + +	r->reorder_buf = +		kcalloc(size, sizeof(struct sk_buff *), GFP_KERNEL); +	r->reorder_time = +		kcalloc(size, sizeof(unsigned long), GFP_KERNEL); +	if (!r->reorder_buf || !r->reorder_time) { +		kfree(r->reorder_buf); +		kfree(r->reorder_time); +		kfree(r); +		return NULL; +	} + +	spin_lock_init(&r->reorder_lock); +	r->ssn = ssn; +	r->head_seq_num = ssn; +	r->buf_size = size; +	r->stored_mpdu_num = 0; +	r->first_time = true; +	return r; +} + +void wil_tid_ampdu_rx_free(struct wil6210_priv *wil, +			   struct wil_tid_ampdu_rx *r) +{ +	if (!r) +		return; +	wil_release_reorder_frames(wil, r, r->head_seq_num + r->buf_size); +	kfree(r->reorder_buf); +	kfree(r->reorder_time); +	kfree(r); +} diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index d505b2676a7..0784ef3d4ce 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -21,6 +21,7 @@  #include <linux/ip.h>  #include <linux/ipv6.h>  #include <net/ipv6.h> +#include <linux/prefetch.h>  #include "wil6210.h"  #include "wmi.h" @@ -63,6 +64,22 @@ static inline int wil_vring_avail_tx(struct vring *vring)  	return vring->size - used - 1;  } +/** + * wil_vring_wmark_low - low watermark for available descriptor space + */ +static inline int wil_vring_wmark_low(struct vring *vring) +{ +	return vring->size/8; +} + +/** + * wil_vring_wmark_high - high watermark for available descriptor space + */ +static inline int wil_vring_wmark_high(struct vring *vring) +{ +	return vring->size/4; +} +  static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring)  {  	struct device *dev = wil_to_dev(wil); @@ -97,12 +114,29 @@ static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring)  		_d->dma.status = TX_DMA_STATUS_DU;  	} -	wil_dbg_misc(wil, "vring[%d] 0x%p:0x%016llx 0x%p\n", vring->size, -		     vring->va, (unsigned long long)vring->pa, vring->ctx); +	wil_dbg_misc(wil, "vring[%d] 0x%p:%pad 0x%p\n", vring->size, +		     vring->va, &vring->pa, vring->ctx);  	return 0;  } +static void wil_txdesc_unmap(struct device *dev, struct vring_tx_desc *d, +			     struct wil_ctx *ctx) +{ +	dma_addr_t pa = wil_desc_addr(&d->dma.addr); +	u16 dmalen = le16_to_cpu(d->dma.length); +	switch (ctx->mapped_as) { +	case wil_mapped_as_single: +		dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE); +		break; +	case wil_mapped_as_page: +		dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE); +		break; +	default: +		break; +	} +} +  static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring,  			   int tx)  { @@ -121,15 +155,7 @@ static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring,  			ctx = &vring->ctx[vring->swtail];  			*d = *_d; -			pa = wil_desc_addr(&d->dma.addr); -			dmalen = le16_to_cpu(d->dma.length); -			if (vring->ctx[vring->swtail].mapped_as_page) { -				dma_unmap_page(dev, pa, dmalen, -					       DMA_TO_DEVICE); -			} else { -				dma_unmap_single(dev, pa, dmalen, -						 DMA_TO_DEVICE); -			} +			wil_txdesc_unmap(dev, d, ctx);  			if (ctx->skb)  				dev_kfree_skb_any(ctx->skb);  			vring->swtail = wil_vring_next_tail(vring); @@ -343,6 +369,9 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,  	u16 dmalen;  	u8 ftype;  	u8 ds_bits; +	int cid; +	struct wil_net_stats *stats; +  	BUILD_BUG_ON(sizeof(struct vring_rx_desc) > sizeof(skb->cb)); @@ -377,11 +406,15 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,  	}  	skb_trim(skb, dmalen); +	prefetch(skb->data); +  	wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1,  			  skb->data, skb_headlen(skb), false); - -	wil->stats.last_mcs_rx = wil_rxdesc_mcs(d); +	cid = wil_rxdesc_cid(d); +	stats = &wil->sta[cid].stats; +	stats->last_mcs_rx = wil_rxdesc_mcs(d); +	wil->stats.last_mcs_rx = stats->last_mcs_rx;  	/* use radiotap header only if required */  	if (ndev->type == ARPHRD_IEEE80211_RADIOTAP) @@ -469,21 +502,28 @@ static int wil_rx_refill(struct wil6210_priv *wil, int count)   * Pass Rx packet to the netif. Update statistics.   * Called in softirq context (NAPI poll).   */ -static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev) +void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)  { -	int rc; +	gro_result_t rc; +	struct wil6210_priv *wil = ndev_to_wil(ndev);  	unsigned int len = skb->len; +	struct vring_rx_desc *d = wil_skb_rxdesc(skb); +	int cid = wil_rxdesc_cid(d); +	struct wil_net_stats *stats = &wil->sta[cid].stats;  	skb_orphan(skb); -	rc = netif_receive_skb(skb); +	rc = napi_gro_receive(&wil->napi_rx, skb); -	if (likely(rc == NET_RX_SUCCESS)) { +	if (unlikely(rc == GRO_DROP)) { +		ndev->stats.rx_dropped++; +		stats->rx_dropped++; +		wil_dbg_txrx(wil, "Rx drop %d bytes\n", len); +	} else {  		ndev->stats.rx_packets++; +		stats->rx_packets++;  		ndev->stats.rx_bytes += len; - -	} else { -		ndev->stats.rx_dropped++; +		stats->rx_bytes += len;  	}  } @@ -512,12 +552,18 @@ void wil_rx_handle(struct wil6210_priv *wil, int *quota)  			skb->ip_summed = CHECKSUM_UNNECESSARY;  			skb->pkt_type = PACKET_OTHERHOST;  			skb->protocol = htons(ETH_P_802_2); - +			wil_netif_rx_any(skb, ndev);  		} else { +			struct ethhdr *eth = (void *)skb->data; +  			skb->protocol = eth_type_trans(skb, ndev); + +			if (is_unicast_ether_addr(eth->h_dest)) +				wil_rx_reorder(wil, skb); +			else +				wil_netif_rx_any(skb, ndev);  		} -		wil_netif_rx_any(skb, ndev);  	}  	wil_rx_refill(wil, v->size);  } @@ -527,6 +573,11 @@ int wil_rx_init(struct wil6210_priv *wil)  	struct vring *vring = &wil->vring_rx;  	int rc; +	if (vring->va) { +		wil_err(wil, "Rx ring already allocated\n"); +		return -EINVAL; +	} +  	vring->size = WIL6210_RX_RING_SIZE;  	rc = wil_vring_alloc(wil, vring);  	if (rc) @@ -567,7 +618,7 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,  				.ring_size = cpu_to_le16(size),  			},  			.ringid = id, -			.cidxtid = (cid & 0xf) | ((tid & 0xf) << 4), +			.cidxtid = mk_cidxtid(cid, tid),  			.encap_trans_type = WMI_VRING_ENC_TYPE_802_3,  			.mac_ctrl = 0,  			.to_resolution = 0, @@ -583,6 +634,7 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,  		struct wmi_vring_cfg_done_event cmd;  	} __packed reply;  	struct vring *vring = &wil->vring_tx[id]; +	struct vring_tx_data *txdata = &wil->vring_tx_data[id];  	if (vring->va) {  		wil_err(wil, "Tx ring [%d] already allocated\n", id); @@ -590,11 +642,15 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,  		goto out;  	} +	memset(txdata, 0, sizeof(*txdata));  	vring->size = size;  	rc = wil_vring_alloc(wil, vring);  	if (rc)  		goto out; +	wil->vring2cid_tid[id][0] = cid; +	wil->vring2cid_tid[id][1] = tid; +  	cmd.vring_cfg.tx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa);  	rc = wmi_call(wil, WMI_VRING_CFG_CMDID, &cmd, sizeof(cmd), @@ -610,6 +666,8 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,  	}  	vring->hwtail = le32_to_cpu(reply.cmd.tx_vring_tail_ptr); +	txdata->enabled = 1; +  	return 0;   out_free:  	wil_vring_free(wil, vring, 1); @@ -622,23 +680,116 @@ void wil_vring_fini_tx(struct wil6210_priv *wil, int id)  {  	struct vring *vring = &wil->vring_tx[id]; +	WARN_ON(!mutex_is_locked(&wil->mutex)); +  	if (!vring->va)  		return; +	/* make sure NAPI won't touch this vring */ +	wil->vring_tx_data[id].enabled = 0; +	if (test_bit(wil_status_napi_en, &wil->status)) +		napi_synchronize(&wil->napi_tx); +  	wil_vring_free(wil, vring, 1);  }  static struct vring *wil_find_tx_vring(struct wil6210_priv *wil,  				       struct sk_buff *skb)  { -	struct vring *v = &wil->vring_tx[0]; +	int i; +	struct ethhdr *eth = (void *)skb->data; +	int cid = wil_find_cid(wil, eth->h_dest); + +	if (cid < 0) +		return NULL; -	if (v->va) -		return v; +	if (!wil->sta[cid].data_port_open && +	    (skb->protocol != cpu_to_be16(ETH_P_PAE))) +		return NULL; + +	/* TODO: fix for multiple TID */ +	for (i = 0; i < ARRAY_SIZE(wil->vring2cid_tid); i++) { +		if (wil->vring2cid_tid[i][0] == cid) { +			struct vring *v = &wil->vring_tx[i]; +			wil_dbg_txrx(wil, "%s(%pM) -> [%d]\n", +				     __func__, eth->h_dest, i); +			if (v->va) { +				return v; +			} else { +				wil_dbg_txrx(wil, "vring[%d] not valid\n", i); +				return NULL; +			} +		} +	}  	return NULL;  } +static void wil_set_da_for_vring(struct wil6210_priv *wil, +				 struct sk_buff *skb, int vring_index) +{ +	struct ethhdr *eth = (void *)skb->data; +	int cid = wil->vring2cid_tid[vring_index][0]; +	memcpy(eth->h_dest, wil->sta[cid].addr, ETH_ALEN); +} + +static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, +			struct sk_buff *skb); +/* + * Find 1-st vring and return it; set dest address for this vring in skb + * duplicate skb and send it to other active vrings + */ +static struct vring *wil_tx_bcast(struct wil6210_priv *wil, +				       struct sk_buff *skb) +{ +	struct vring *v, *v2; +	struct sk_buff *skb2; +	int i; +	u8 cid; + +	/* find 1-st vring eligible for data */ +	for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) { +		v = &wil->vring_tx[i]; +		if (!v->va) +			continue; + +		cid = wil->vring2cid_tid[i][0]; +		if (!wil->sta[cid].data_port_open) +			continue; + +		goto found; +	} + +	wil_err(wil, "Tx while no vrings active?\n"); + +	return NULL; + +found: +	wil_dbg_txrx(wil, "BCAST -> ring %d\n", i); +	wil_set_da_for_vring(wil, skb, i); + +	/* find other active vrings and duplicate skb for each */ +	for (i++; i < WIL6210_MAX_TX_RINGS; i++) { +		v2 = &wil->vring_tx[i]; +		if (!v2->va) +			continue; +		cid = wil->vring2cid_tid[i][0]; +		if (!wil->sta[cid].data_port_open) +			continue; + +		skb2 = skb_copy(skb, GFP_ATOMIC); +		if (skb2) { +			wil_dbg_txrx(wil, "BCAST DUP -> ring %d\n", i); +			wil_set_da_for_vring(wil, skb2, i); +			wil_tx_vring(wil, v2, skb2); +		} else { +			wil_err(wil, "skb_copy failed\n"); +		} +	} + +	return v; +} +  static int wil_tx_desc_map(struct vring_tx_desc *d, dma_addr_t pa, u32 len,  			   int vring_index)  { @@ -664,6 +815,13 @@ static int wil_tx_desc_map(struct vring_tx_desc *d, dma_addr_t pa, u32 len,  	return 0;  } +static inline +void wil_tx_desc_set_nr_frags(struct vring_tx_desc *d, int nr_frags) +{ +	d->mac.d[2] |= ((nr_frags + 1) << +		       MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS); +} +  static int wil_tx_desc_offload_cksum_set(struct wil6210_priv *wil,  				struct vring_tx_desc *d,  				struct sk_buff *skb) @@ -673,9 +831,12 @@ static int wil_tx_desc_offload_cksum_set(struct wil6210_priv *wil,  	if (skb->ip_summed != CHECKSUM_PARTIAL)  		return 0; +	d->dma.b11 = ETH_HLEN; /* MAC header length */ +  	switch (skb->protocol) {  	case cpu_to_be16(ETH_P_IP):  		protocol = ip_hdr(skb)->protocol; +		d->dma.b11 |= BIT(DMA_CFG_DESC_TX_OFFLOAD_CFG_L3T_IPV4_POS);  		break;  	case cpu_to_be16(ETH_P_IPV6):  		protocol = ipv6_hdr(skb)->nexthdr; @@ -701,8 +862,6 @@ static int wil_tx_desc_offload_cksum_set(struct wil6210_priv *wil,  	}  	d->dma.ip_length = skb_network_header_len(skb); -	d->dma.b11 = ETH_HLEN; /* MAC header length */ -	d->dma.b11 |= BIT(DMA_CFG_DESC_TX_OFFLOAD_CFG_L3T_IPV4_POS);  	/* Enable TCP/UDP checksum */  	d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_TCP_UDP_CHECKSUM_EN_POS);  	/* Calculate pseudo-header */ @@ -727,8 +886,6 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,  	wil_dbg_txrx(wil, "%s()\n", __func__); -	if (avail < vring->size/8) -		netif_tx_stop_all_queues(wil_to_ndev(wil));  	if (avail < 1 + nr_frags) {  		wil_err(wil, "Tx ring full. No space for %d fragments\n",  			1 + nr_frags); @@ -736,19 +893,17 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,  	}  	_d = &(vring->va[i].tx); -	/* FIXME FW can accept only unicast frames for the peer */ -	memcpy(skb->data, wil->dst_addr[vring_index], ETH_ALEN); -  	pa = dma_map_single(dev, skb->data,  			skb_headlen(skb), DMA_TO_DEVICE); -	wil_dbg_txrx(wil, "Tx skb %d bytes %p -> %#08llx\n", skb_headlen(skb), -		     skb->data, (unsigned long long)pa); +	wil_dbg_txrx(wil, "Tx skb %d bytes 0x%p -> %pad\n", skb_headlen(skb), +		     skb->data, &pa);  	wil_hex_dump_txrx("Tx ", DUMP_PREFIX_OFFSET, 16, 1,  			  skb->data, skb_headlen(skb), false);  	if (unlikely(dma_mapping_error(dev, pa)))  		return -EINVAL; +	vring->ctx[i].mapped_as = wil_mapped_as_single;  	/* 1-st segment */  	wil_tx_desc_map(d, pa, skb_headlen(skb), vring_index);  	/* Process TCP/UDP checksum offloading */ @@ -758,8 +913,8 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,  		goto dma_error;  	} -	d->mac.d[2] |= ((nr_frags + 1) << -		       MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS); +	vring->ctx[i].nr_frags = nr_frags; +	wil_tx_desc_set_nr_frags(d, nr_frags);  	if (nr_frags)  		*_d = *d; @@ -774,8 +929,13 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,  				DMA_TO_DEVICE);  		if (unlikely(dma_mapping_error(dev, pa)))  			goto dma_error; +		vring->ctx[i].mapped_as = wil_mapped_as_page;  		wil_tx_desc_map(d, pa, len, vring_index); -		vring->ctx[i].mapped_as_page = 1; +		/* no need to check return code - +		 * if it succeeded for 1-st descriptor, +		 * it will succeed here too +		 */ +		wil_tx_desc_offload_cksum_set(wil, d, skb);  		*_d = *d;  	}  	/* for the last seg only */ @@ -804,7 +964,6 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,  	/* unmap what we have mapped */  	nr_frags = f + 1; /* frags mapped + one for skb head */  	for (f = 0; f < nr_frags; f++) { -		u16 dmalen;  		struct wil_ctx *ctx;  		i = (swhead + f) % vring->size; @@ -812,12 +971,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,  		_d = &(vring->va[i].tx);  		*d = *_d;  		_d->dma.status = TX_DMA_STATUS_DU; -		pa = wil_desc_addr(&d->dma.addr); -		dmalen = le16_to_cpu(d->dma.length); -		if (ctx->mapped_as_page) -			dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE); -		else -			dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE); +		wil_txdesc_unmap(dev, d, ctx);  		if (ctx->skb)  			dev_kfree_skb_any(ctx->skb); @@ -832,12 +986,17 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,  netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)  {  	struct wil6210_priv *wil = ndev_to_wil(ndev); +	struct ethhdr *eth = (void *)skb->data;  	struct vring *vring; +	static bool pr_once_fw;  	int rc;  	wil_dbg_txrx(wil, "%s()\n", __func__);  	if (!test_bit(wil_status_fwready, &wil->status)) { -		wil_err(wil, "FW not ready\n"); +		if (!pr_once_fw) { +			wil_err(wil, "FW not ready\n"); +			pr_once_fw = true; +		}  		goto drop;  	}  	if (!test_bit(wil_status_fwconnected, &wil->status)) { @@ -848,16 +1007,25 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)  		wil_err(wil, "Xmit in monitor mode not supported\n");  		goto drop;  	} +	pr_once_fw = false;  	/* find vring */ -	vring = wil_find_tx_vring(wil, skb); +	if (is_unicast_ether_addr(eth->h_dest)) { +		vring = wil_find_tx_vring(wil, skb); +	} else { +		vring = wil_tx_bcast(wil, skb); +	}  	if (!vring) { -		wil_err(wil, "No Tx VRING available\n"); +		wil_err(wil, "No Tx VRING found for %pM\n", eth->h_dest);  		goto drop;  	}  	/* set up vring entry */  	rc = wil_tx_vring(wil, vring, skb); +	/* do we still have enough room in the vring? */ +	if (wil_vring_avail_tx(vring) < wil_vring_wmark_low(vring)) +		netif_tx_stop_all_queues(wil_to_ndev(wil)); +  	switch (rc) {  	case 0:  		/* statistics will be updated on the tx_complete */ @@ -887,66 +1055,84 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid)  	struct net_device *ndev = wil_to_ndev(wil);  	struct device *dev = wil_to_dev(wil);  	struct vring *vring = &wil->vring_tx[ringid]; +	struct vring_tx_data *txdata = &wil->vring_tx_data[ringid];  	int done = 0; +	int cid = wil->vring2cid_tid[ringid][0]; +	struct wil_net_stats *stats = &wil->sta[cid].stats; +	volatile struct vring_tx_desc *_d;  	if (!vring->va) {  		wil_err(wil, "Tx irq[%d]: vring not initialized\n", ringid);  		return 0;  	} +	if (!txdata->enabled) { +		wil_info(wil, "Tx irq[%d]: vring disabled\n", ringid); +		return 0; +	} +  	wil_dbg_txrx(wil, "%s(%d)\n", __func__, ringid);  	while (!wil_vring_is_empty(vring)) { -		volatile struct vring_tx_desc *_d = -					      &vring->va[vring->swtail].tx; -		struct vring_tx_desc dd, *d = ⅆ -		dma_addr_t pa; -		u16 dmalen; +		int new_swtail;  		struct wil_ctx *ctx = &vring->ctx[vring->swtail]; -		struct sk_buff *skb = ctx->skb; - -		*d = *_d; +		/** +		 * For the fragmented skb, HW will set DU bit only for the +		 * last fragment. look for it +		 */ +		int lf = (vring->swtail + ctx->nr_frags) % vring->size; +		/* TODO: check we are not past head */ -		if (!(d->dma.status & TX_DMA_STATUS_DU)) +		_d = &vring->va[lf].tx; +		if (!(_d->dma.status & TX_DMA_STATUS_DU))  			break; -		dmalen = le16_to_cpu(d->dma.length); -		trace_wil6210_tx_done(ringid, vring->swtail, dmalen, -				      d->dma.error); -		wil_dbg_txrx(wil, -			     "Tx[%3d] : %d bytes, status 0x%02x err 0x%02x\n", -			     vring->swtail, dmalen, d->dma.status, -			     d->dma.error); -		wil_hex_dump_txrx("TxC ", DUMP_PREFIX_NONE, 32, 4, -				  (const void *)d, sizeof(*d), false); - -		pa = wil_desc_addr(&d->dma.addr); -		if (ctx->mapped_as_page) -			dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE); -		else -			dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE); - -		if (skb) { -			if (d->dma.error == 0) { -				ndev->stats.tx_packets++; -				ndev->stats.tx_bytes += skb->len; -			} else { -				ndev->stats.tx_errors++; -			} +		new_swtail = (lf + 1) % vring->size; +		while (vring->swtail != new_swtail) { +			struct vring_tx_desc dd, *d = ⅆ +			u16 dmalen; +			struct wil_ctx *ctx = &vring->ctx[vring->swtail]; +			struct sk_buff *skb = ctx->skb; +			_d = &vring->va[vring->swtail].tx; + +			*d = *_d; + +			dmalen = le16_to_cpu(d->dma.length); +			trace_wil6210_tx_done(ringid, vring->swtail, dmalen, +					      d->dma.error); +			wil_dbg_txrx(wil, +				     "Tx[%3d] : %d bytes, status 0x%02x err 0x%02x\n", +				     vring->swtail, dmalen, d->dma.status, +				     d->dma.error); +			wil_hex_dump_txrx("TxC ", DUMP_PREFIX_NONE, 32, 4, +					  (const void *)d, sizeof(*d), false); + +			wil_txdesc_unmap(dev, d, ctx); + +			if (skb) { +				if (d->dma.error == 0) { +					ndev->stats.tx_packets++; +					stats->tx_packets++; +					ndev->stats.tx_bytes += skb->len; +					stats->tx_bytes += skb->len; +				} else { +					ndev->stats.tx_errors++; +					stats->tx_errors++; +				} -			dev_kfree_skb_any(skb); +				dev_kfree_skb_any(skb); +			} +			memset(ctx, 0, sizeof(*ctx)); +			/* There is no need to touch HW descriptor: +			 * - ststus bit TX_DMA_STATUS_DU is set by design, +			 *   so hardware will not try to process this desc., +			 * - rest of descriptor will be initialized on Tx. +			 */ +			vring->swtail = wil_vring_next_tail(vring); +			done++;  		} -		memset(ctx, 0, sizeof(*ctx)); -		/* -		 * There is no need to touch HW descriptor: -		 * - ststus bit TX_DMA_STATUS_DU is set by design, -		 *   so hardware will not try to process this desc., -		 * - rest of descriptor will be initialized on Tx. -		 */ -		vring->swtail = wil_vring_next_tail(vring); -		done++;  	} -	if (wil_vring_avail_tx(vring) > vring->size/4) +	if (wil_vring_avail_tx(vring) > wil_vring_wmark_high(vring))  		netif_tx_wake_all_queues(wil_to_ndev(wil));  	return done; diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h index b3828279204..bc5706a4f00 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.h +++ b/drivers/net/wireless/ath/wil6210/txrx.h @@ -436,4 +436,11 @@ static inline struct vring_rx_desc *wil_skb_rxdesc(struct sk_buff *skb)  	return (void *)skb->cb;  } +void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev); +void wil_rx_reorder(struct wil6210_priv *wil, struct sk_buff *skb); +struct wil_tid_ampdu_rx *wil_tid_ampdu_rx_alloc(struct wil6210_priv *wil, +						int size, u16 ssn); +void wil_tid_ampdu_rx_free(struct wil6210_priv *wil, +			   struct wil_tid_ampdu_rx *r); +  #endif /* WIL6210_TXRX_H */ diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index c4a51638736..e25edc52398 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -35,10 +35,14 @@ static inline u32 WIL_GET_BITS(u32 x, int b0, int b1)  #define WIL6210_MEM_SIZE (2*1024*1024UL)  #define WIL6210_RX_RING_SIZE	(128) -#define WIL6210_TX_RING_SIZE	(128) +#define WIL6210_TX_RING_SIZE	(512)  #define WIL6210_MAX_TX_RINGS	(24) /* HW limit */  #define WIL6210_MAX_CID		(8) /* HW limit */  #define WIL6210_NAPI_BUDGET	(16) /* arbitrary */ +#define WIL6210_ITR_TRSH	(10000) /* arbitrary - about 15 IRQs/msec */ +#define WIL6210_FW_RECOVERY_RETRIES	(5) /* try to recover this many times */ +#define WIL6210_FW_RECOVERY_TO	msecs_to_jiffies(5000) +#define WIL6210_SCAN_TO		msecs_to_jiffies(10000)  /* Hardware definitions begin */ @@ -73,23 +77,21 @@ struct RGF_ICR {  } __packed;  /* registers - FW addresses */ -#define RGF_USER_USER_SCRATCH_PAD	(0x8802bc) -#define RGF_USER_USER_ICR		(0x880b4c) /* struct RGF_ICR */ -	#define BIT_USER_USER_ICR_SW_INT_2	BIT(18) -#define RGF_USER_CLKS_CTL_SW_RST_MASK_0	(0x880b14) -#define RGF_USER_MAC_CPU_0		(0x8801fc) +#define RGF_USER_HW_MACHINE_STATE	(0x8801dc) +	#define HW_MACHINE_BOOT_DONE	(0x3fffffd)  #define RGF_USER_USER_CPU_0		(0x8801e0) +#define RGF_USER_MAC_CPU_0		(0x8801fc) +#define RGF_USER_USER_SCRATCH_PAD	(0x8802bc) +#define RGF_USER_FW_REV_ID		(0x880a8c) /* chip revision */ +#define RGF_USER_CLKS_CTL_0		(0x880abc) +	#define BIT_USER_CLKS_RST_PWGD	BIT(11) /* reset on "power good" */  #define RGF_USER_CLKS_CTL_SW_RST_VEC_0	(0x880b04)  #define RGF_USER_CLKS_CTL_SW_RST_VEC_1	(0x880b08)  #define RGF_USER_CLKS_CTL_SW_RST_VEC_2	(0x880b0c)  #define RGF_USER_CLKS_CTL_SW_RST_VEC_3	(0x880b10) - -#define RGF_DMA_PSEUDO_CAUSE		(0x881c68) -#define RGF_DMA_PSEUDO_CAUSE_MASK_SW	(0x881c6c) -#define RGF_DMA_PSEUDO_CAUSE_MASK_FW	(0x881c70) -	#define BIT_DMA_PSEUDO_CAUSE_RX		BIT(0) -	#define BIT_DMA_PSEUDO_CAUSE_TX		BIT(1) -	#define BIT_DMA_PSEUDO_CAUSE_MISC	BIT(2) +#define RGF_USER_CLKS_CTL_SW_RST_MASK_0	(0x880b14) +#define RGF_USER_USER_ICR		(0x880b4c) /* struct RGF_ICR */ +	#define BIT_USER_USER_ICR_SW_INT_2	BIT(18)  #define RGF_DMA_EP_TX_ICR		(0x881bb4) /* struct RGF_ICR */  	#define BIT_DMA_EP_TX_ICR_TX_DONE	BIT(0) @@ -104,13 +106,22 @@ struct RGF_ICR {  /* Interrupt moderation control */  #define RGF_DMA_ITR_CNT_TRSH		(0x881c5c)  #define RGF_DMA_ITR_CNT_DATA		(0x881c60) -#define RGF_DMA_ITR_CNT_CRL		(0x881C64) +#define RGF_DMA_ITR_CNT_CRL		(0x881c64)  	#define BIT_DMA_ITR_CNT_CRL_EN		BIT(0)  	#define BIT_DMA_ITR_CNT_CRL_EXT_TICK	BIT(1)  	#define BIT_DMA_ITR_CNT_CRL_FOREVER	BIT(2)  	#define BIT_DMA_ITR_CNT_CRL_CLR		BIT(3)  	#define BIT_DMA_ITR_CNT_CRL_REACH_TRSH	BIT(4) +#define RGF_DMA_PSEUDO_CAUSE		(0x881c68) +#define RGF_DMA_PSEUDO_CAUSE_MASK_SW	(0x881c6c) +#define RGF_DMA_PSEUDO_CAUSE_MASK_FW	(0x881c70) +	#define BIT_DMA_PSEUDO_CAUSE_RX		BIT(0) +	#define BIT_DMA_PSEUDO_CAUSE_TX		BIT(1) +	#define BIT_DMA_PSEUDO_CAUSE_MISC	BIT(2) + +#define RGF_PCIE_LOS_COUNTER_CTL	(0x882dc4) +  /* popular locations */  #define HOST_MBOX   HOSTADDR(RGF_USER_USER_SCRATCH_PAD)  #define HOST_SW_INT (HOSTADDR(RGF_USER_USER_ICR) + \ @@ -124,6 +135,31 @@ struct RGF_ICR {  /* Hardware definitions end */ +/** + * mk_cidxtid - construct @cidxtid field + * @cid: CID value + * @tid: TID value + * + * @cidxtid field encoded as bits 0..3 - CID; 4..7 - TID + */ +static inline u8 mk_cidxtid(u8 cid, u8 tid) +{ +	return ((tid & 0xf) << 4) | (cid & 0xf); +} + +/** + * parse_cidxtid - parse @cidxtid field + * @cid: store CID value here + * @tid: store TID value here + * + * @cidxtid field encoded as bits 0..3 - CID; 4..7 - TID + */ +static inline void parse_cidxtid(u8 cidxtid, u8 *cid, u8 *tid) +{ +	*cid = cidxtid & 0xf; +	*tid = (cidxtid >> 4) & 0xf; +} +  struct wil6210_mbox_ring {  	u32 base;  	u16 entry_size; /* max. size of mbox entry, incl. all headers */ @@ -183,12 +219,19 @@ struct pending_wmi_event {  	} __packed event;  }; +enum { /* for wil_ctx.mapped_as */ +	wil_mapped_as_none = 0, +	wil_mapped_as_single = 1, +	wil_mapped_as_page = 2, +}; +  /**   * struct wil_ctx - software context for Vring descriptor   */  struct wil_ctx {  	struct sk_buff *skb; -	u8 mapped_as_page:1; +	u8 nr_frags; +	u8 mapped_as;  };  union vring_desc; @@ -203,6 +246,14 @@ struct vring {  	struct wil_ctx *ctx; /* ctx[size] - software context */  }; +/** + * Additional data for Tx Vring + */ +struct vring_tx_data { +	int enabled; + +}; +  enum { /* for wil6210_priv.status */  	wil_status_fwready = 0,  	wil_status_fwconnecting, @@ -210,10 +261,52 @@ enum { /* for wil6210_priv.status */  	wil_status_dontscan,  	wil_status_reset_done,  	wil_status_irqen, /* FIXME: interrupts enabled - for debug */ +	wil_status_napi_en, /* NAPI enabled protected by wil->mutex */  };  struct pci_dev; +/** + * struct tid_ampdu_rx - TID aggregation information (Rx). + * + * @reorder_buf: buffer to reorder incoming aggregated MPDUs + * @reorder_time: jiffies when skb was added + * @session_timer: check if peer keeps Tx-ing on the TID (by timeout value) + * @reorder_timer: releases expired frames from the reorder buffer. + * @last_rx: jiffies of last rx activity + * @head_seq_num: head sequence number in reordering buffer. + * @stored_mpdu_num: number of MPDUs in reordering buffer + * @ssn: Starting Sequence Number expected to be aggregated. + * @buf_size: buffer size for incoming A-MPDUs + * @timeout: reset timer value (in TUs). + * @dialog_token: dialog token for aggregation session + * @rcu_head: RCU head used for freeing this struct + * @reorder_lock: serializes access to reorder buffer, see below. + * + * This structure's lifetime is managed by RCU, assignments to + * the array holding it must hold the aggregation mutex. + * + * The @reorder_lock is used to protect the members of this + * struct, except for @timeout, @buf_size and @dialog_token, + * which are constant across the lifetime of the struct (the + * dialog token being used only for debugging). + */ +struct wil_tid_ampdu_rx { +	spinlock_t reorder_lock; /* see above */ +	struct sk_buff **reorder_buf; +	unsigned long *reorder_time; +	struct timer_list session_timer; +	struct timer_list reorder_timer; +	unsigned long last_rx; +	u16 head_seq_num; +	u16 stored_mpdu_num; +	u16 ssn; +	u16 buf_size; +	u16 timeout; +	u8 dialog_token; +	bool first_time; /* is it 1-st time this buffer used? */ +}; +  struct wil6210_stats {  	u64 tsf;  	u32 snr; @@ -225,6 +318,43 @@ struct wil6210_stats {  	u16 peer_tx_sector;  }; +enum wil_sta_status { +	wil_sta_unused = 0, +	wil_sta_conn_pending = 1, +	wil_sta_connected = 2, +}; + +#define WIL_STA_TID_NUM (16) + +struct wil_net_stats { +	unsigned long	rx_packets; +	unsigned long	tx_packets; +	unsigned long	rx_bytes; +	unsigned long	tx_bytes; +	unsigned long	tx_errors; +	unsigned long	rx_dropped; +	u16 last_mcs_rx; +}; + +/** + * struct wil_sta_info - data for peer + * + * Peer identified by its CID (connection ID) + * NIC performs beam forming for each peer; + * if no beam forming done, frame exchange is not + * possible. + */ +struct wil_sta_info { +	u8 addr[ETH_ALEN]; +	enum wil_sta_status status; +	struct wil_net_stats stats; +	bool data_port_open; /* can send any data, not only EAPOL */ +	/* Rx BACK */ +	struct wil_tid_ampdu_rx *tid_rx[WIL_STA_TID_NUM]; +	unsigned long tid_rx_timer_expired[BITS_TO_LONGS(WIL_STA_TID_NUM)]; +	unsigned long tid_rx_stop_requested[BITS_TO_LONGS(WIL_STA_TID_NUM)]; +}; +  struct wil6210_priv {  	struct pci_dev *pdev;  	int n_msi; @@ -232,7 +362,10 @@ struct wil6210_priv {  	void __iomem *csr;  	ulong status;  	u32 fw_version; +	u32 hw_version;  	u8 n_mids; /* number of additional MIDs as reported by FW */ +	int recovery_count; /* num of FW recovery attempts in a short time */ +	unsigned long last_fw_recovery; /* jiffies of last fw recovery */  	/* profile */  	u32 monitor_flags;  	u32 secure_pcp; /* create secure PCP? */ @@ -252,7 +385,9 @@ struct wil6210_priv {  	struct workqueue_struct *wmi_wq_conn; /* for connect worker */  	struct work_struct connect_worker;  	struct work_struct disconnect_worker; +	struct work_struct fw_error_worker;	/* for FW error recovery */  	struct timer_list connect_timer; +	struct timer_list scan_timer; /* detect scan timeout */  	int pending_connect_cid;  	struct list_head pending_wmi_ev;  	/* @@ -266,7 +401,9 @@ struct wil6210_priv {  	/* DMA related */  	struct vring vring_rx;  	struct vring vring_tx[WIL6210_MAX_TX_RINGS]; -	u8 dst_addr[WIL6210_MAX_TX_RINGS][ETH_ALEN]; +	struct vring_tx_data vring_tx_data[WIL6210_MAX_TX_RINGS]; +	u8 vring2cid_tid[WIL6210_MAX_TX_RINGS][2]; /* [0] - CID, [1] - TID */ +	struct wil_sta_info sta[WIL6210_MAX_CID];  	/* scan */  	struct cfg80211_scan_request *scan_request; @@ -328,11 +465,13 @@ void wil_if_remove(struct wil6210_priv *wil);  int wil_priv_init(struct wil6210_priv *wil);  void wil_priv_deinit(struct wil6210_priv *wil);  int wil_reset(struct wil6210_priv *wil); +void wil_fw_error_recovery(struct wil6210_priv *wil);  void wil_link_on(struct wil6210_priv *wil);  void wil_link_off(struct wil6210_priv *wil);  int wil_up(struct wil6210_priv *wil);  int wil_down(struct wil6210_priv *wil);  void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r); +int wil_find_cid(struct wil6210_priv *wil, const u8 *mac);  void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr);  void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr); @@ -356,8 +495,11 @@ int wmi_echo(struct wil6210_priv *wil);  int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie);  int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring);  int wmi_p2p_cfg(struct wil6210_priv *wil, int channel); +int wmi_rxon(struct wil6210_priv *wil, bool on);  int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_m, u32 *t_r); +int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac, u16 reason); +void wil6210_clear_irq(struct wil6210_priv *wil);  int wil6210_init_irq(struct wil6210_priv *wil, int irq);  void wil6210_fini_irq(struct wil6210_priv *wil, int irq);  void wil6210_disable_irq(struct wil6210_priv *wil); @@ -372,7 +514,7 @@ void wil_wdev_free(struct wil6210_priv *wil);  int wmi_set_mac_address(struct wil6210_priv *wil, void *addr);  int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan);  int wmi_pcp_stop(struct wil6210_priv *wil); -void wil6210_disconnect(struct wil6210_priv *wil, void *bssid); +void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid);  int wil_rx_init(struct wil6210_priv *wil);  void wil_rx_fini(struct wil6210_priv *wil); diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 063963ee422..6cc0e182cc7 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -192,7 +192,7 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)  	might_sleep();  	if (!test_bit(wil_status_fwready, &wil->status)) { -		wil_err(wil, "FW not ready\n"); +		wil_err(wil, "WMI: cannot send command while FW not ready\n");  		return -EAGAIN;  	} @@ -276,8 +276,8 @@ static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len)  	wil->fw_version = le32_to_cpu(evt->sw_version);  	wil->n_mids = evt->numof_additional_mids; -	wil_dbg_wmi(wil, "FW ver. %d; MAC %pM; %d MID's\n", wil->fw_version, -		    evt->mac, wil->n_mids); +	wil_info(wil, "FW ver. %d; MAC %pM; %d MID's\n", wil->fw_version, +		 evt->mac, wil->n_mids);  	if (!is_valid_ether_addr(ndev->dev_addr)) {  		memcpy(ndev->dev_addr, evt->mac, ETH_ALEN); @@ -290,7 +290,7 @@ static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len)  static void wmi_evt_fw_ready(struct wil6210_priv *wil, int id, void *d,  			     int len)  { -	wil_dbg_wmi(wil, "WMI: FW ready\n"); +	wil_dbg_wmi(wil, "WMI: got FW ready event\n");  	set_bit(wil_status_fwready, &wil->status);  	/* reuse wmi_ready for the firmware ready indication */ @@ -307,14 +307,14 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len)  	u32 freq = ieee80211_channel_to_frequency(ch_no,  			IEEE80211_BAND_60GHZ);  	struct ieee80211_channel *channel = ieee80211_get_channel(wiphy, freq); -	/* TODO convert LE to CPU */ -	s32 signal = 0; /* TODO */ +	s32 signal = data->info.sqi;  	__le16 fc = rx_mgmt_frame->frame_control;  	u32 d_len = le32_to_cpu(data->info.len);  	u16 d_status = le16_to_cpu(data->info.status); -	wil_dbg_wmi(wil, "MGMT: channel %d MCS %d SNR %d\n", -		    data->info.channel, data->info.mcs, data->info.snr); +	wil_dbg_wmi(wil, "MGMT: channel %d MCS %d SNR %d SQI %d%%\n", +		    data->info.channel, data->info.mcs, data->info.snr, +		    data->info.sqi);  	wil_dbg_wmi(wil, "status 0x%04x len %d fc 0x%04x\n", d_status, d_len,  		    le16_to_cpu(fc));  	wil_dbg_wmi(wil, "qid %d mid %d cid %d\n", @@ -348,9 +348,10 @@ static void wmi_evt_scan_complete(struct wil6210_priv *wil, int id,  {  	if (wil->scan_request) {  		struct wmi_scan_complete_event *data = d; -		bool aborted = (data->status != 0); +		bool aborted = (data->status != WMI_SCAN_SUCCESS);  		wil_dbg_wmi(wil, "SCAN_COMPLETE(0x%08x)\n", data->status); +		del_timer_sync(&wil->scan_timer);  		cfg80211_scan_done(wil->scan_request, aborted);  		wil->scan_request = NULL;  	} else { @@ -384,6 +385,11 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len)  			evt->assoc_req_len, evt->assoc_resp_len);  		return;  	} +	if (evt->cid >= WIL6210_MAX_CID) { +		wil_err(wil, "Connect CID invalid : %d\n", evt->cid); +		return; +	} +  	ch = evt->channel + 1;  	wil_dbg_wmi(wil, "Connect %pM channel [%d] cid %d\n",  		    evt->bssid, ch, evt->cid); @@ -439,7 +445,8 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len)  	/* FIXME FW can transmit only ucast frames to peer */  	/* FIXME real ring_id instead of hard coded 0 */ -	memcpy(wil->dst_addr[0], evt->bssid, ETH_ALEN); +	memcpy(wil->sta[evt->cid].addr, evt->bssid, ETH_ALEN); +	wil->sta[evt->cid].status = wil_sta_conn_pending;  	wil->pending_connect_cid = evt->cid;  	queue_work(wil->wmi_wq_conn, &wil->connect_worker); @@ -456,7 +463,9 @@ static void wmi_evt_disconnect(struct wil6210_priv *wil, int id,  	wil->sinfo_gen++; +	mutex_lock(&wil->mutex);  	wil6210_disconnect(wil, evt->bssid); +	mutex_unlock(&wil->mutex);  }  static void wmi_evt_notify(struct wil6210_priv *wil, int id, void *d, int len) @@ -476,11 +485,11 @@ static void wmi_evt_notify(struct wil6210_priv *wil, int id, void *d, int len)  	wil->stats.peer_rx_sector = le16_to_cpu(evt->other_rx_sector);  	wil->stats.peer_tx_sector = le16_to_cpu(evt->other_tx_sector);  	wil_dbg_wmi(wil, "Link status, MCS %d TSF 0x%016llx\n" -		    "BF status 0x%08x SNR 0x%08x\n" +		    "BF status 0x%08x SNR 0x%08x SQI %d%%\n"  		    "Tx Tpt %d goodput %d Rx goodput %d\n"  		    "Sectors(rx:tx) my %d:%d peer %d:%d\n",  		    wil->stats.bf_mcs, wil->stats.tsf, evt->status, -		    wil->stats.snr, le32_to_cpu(evt->tx_tpt), +		    wil->stats.snr, evt->sqi, le32_to_cpu(evt->tx_tpt),  		    le32_to_cpu(evt->tx_goodput), le32_to_cpu(evt->rx_goodput),  		    wil->stats.my_rx_sector, wil->stats.my_tx_sector,  		    wil->stats.peer_rx_sector, wil->stats.peer_tx_sector); @@ -499,10 +508,16 @@ static void wmi_evt_eapol_rx(struct wil6210_priv *wil, int id,  	int sz = eapol_len + ETH_HLEN;  	struct sk_buff *skb;  	struct ethhdr *eth; +	int cid; +	struct wil_net_stats *stats = NULL;  	wil_dbg_wmi(wil, "EAPOL len %d from %pM\n", eapol_len,  		    evt->src_mac); +	cid = wil_find_cid(wil, evt->src_mac); +	if (cid >= 0) +		stats = &wil->sta[cid].stats; +  	if (eapol_len > 196) { /* TODO: revisit size limit */  		wil_err(wil, "EAPOL too large\n");  		return; @@ -513,6 +528,7 @@ static void wmi_evt_eapol_rx(struct wil6210_priv *wil, int id,  		wil_err(wil, "Failed to allocate skb\n");  		return;  	} +  	eth = (struct ethhdr *)skb_put(skb, ETH_HLEN);  	memcpy(eth->h_dest, ndev->dev_addr, ETH_ALEN);  	memcpy(eth->h_source, evt->src_mac, ETH_ALEN); @@ -521,9 +537,15 @@ static void wmi_evt_eapol_rx(struct wil6210_priv *wil, int id,  	skb->protocol = eth_type_trans(skb, ndev);  	if (likely(netif_rx_ni(skb) == NET_RX_SUCCESS)) {  		ndev->stats.rx_packets++; -		ndev->stats.rx_bytes += skb->len; +		ndev->stats.rx_bytes += sz; +		if (stats) { +			stats->rx_packets++; +			stats->rx_bytes += sz; +		}  	} else {  		ndev->stats.rx_dropped++; +		if (stats) +			stats->rx_dropped++;  	}  } @@ -531,9 +553,16 @@ static void wmi_evt_linkup(struct wil6210_priv *wil, int id, void *d, int len)  {  	struct net_device *ndev = wil_to_ndev(wil);  	struct wmi_data_port_open_event *evt = d; +	u8 cid = evt->cid; + +	wil_dbg_wmi(wil, "Link UP for CID %d\n", cid); -	wil_dbg_wmi(wil, "Link UP for CID %d\n", evt->cid); +	if (cid >= ARRAY_SIZE(wil->sta)) { +		wil_err(wil, "Link UP for invalid CID %d\n", cid); +		return; +	} +	wil->sta[cid].data_port_open = true;  	netif_carrier_on(ndev);  } @@ -541,10 +570,17 @@ static void wmi_evt_linkdown(struct wil6210_priv *wil, int id, void *d, int len)  {  	struct net_device *ndev = wil_to_ndev(wil);  	struct wmi_wbe_link_down_event *evt = d; +	u8 cid = evt->cid;  	wil_dbg_wmi(wil, "Link DOWN for CID %d, reason %d\n", -		    evt->cid, le32_to_cpu(evt->reason)); +		    cid, le32_to_cpu(evt->reason)); +	if (cid >= ARRAY_SIZE(wil->sta)) { +		wil_err(wil, "Link DOWN for invalid CID %d\n", cid); +		return; +	} + +	wil->sta[cid].data_port_open = false;  	netif_carrier_off(ndev);  } @@ -552,10 +588,42 @@ static void wmi_evt_ba_status(struct wil6210_priv *wil, int id, void *d,  			      int len)  {  	struct wmi_vring_ba_status_event *evt = d; +	struct wil_sta_info *sta; +	uint i, cid; + +	/* TODO: use Rx BA status, not Tx one */  	wil_dbg_wmi(wil, "BACK[%d] %s {%d} timeout %d\n", -		    evt->ringid, evt->status ? "N/A" : "OK", evt->agg_wsize, -		    __le16_to_cpu(evt->ba_timeout)); +		    evt->ringid, +		    evt->status == WMI_BA_AGREED ? "OK" : "N/A", +		    evt->agg_wsize, __le16_to_cpu(evt->ba_timeout)); + +	if (evt->ringid >= WIL6210_MAX_TX_RINGS) { +		wil_err(wil, "invalid ring id %d\n", evt->ringid); +		return; +	} + +	cid = wil->vring2cid_tid[evt->ringid][0]; +	if (cid >= WIL6210_MAX_CID) { +		wil_err(wil, "invalid CID %d for vring %d\n", cid, evt->ringid); +		return; +	} + +	sta = &wil->sta[cid]; +	if (sta->status == wil_sta_unused) { +		wil_err(wil, "CID %d unused\n", cid); +		return; +	} + +	wil_dbg_wmi(wil, "BACK for CID %d %pM\n", cid, sta->addr); +	for (i = 0; i < WIL_STA_TID_NUM; i++) { +		struct wil_tid_ampdu_rx *r = sta->tid_rx[i]; +		sta->tid_rx[i] = NULL; +		wil_tid_ampdu_rx_free(wil, r); +		if ((evt->status == WMI_BA_AGREED) && evt->agg_wsize) +			sta->tid_rx[i] = wil_tid_ampdu_rx_alloc(wil, +						evt->agg_wsize, 0); +	}  }  static const struct { @@ -591,21 +659,27 @@ void wmi_recv_cmd(struct wil6210_priv *wil)  	u8 *cmd;  	void __iomem *src;  	ulong flags; +	unsigned n;  	if (!test_bit(wil_status_reset_done, &wil->status)) {  		wil_err(wil, "Reset not completed\n");  		return;  	} -	for (;;) { +	for (n = 0;; n++) {  		u16 len;  		r->head = ioread32(wil->csr + HOST_MBOX +  				   offsetof(struct wil6210_mbox_ctl, rx.head)); -		if (r->tail == r->head) +		if (r->tail == r->head) { +			if (n == 0) +				wil_dbg_wmi(wil, "No events?\n");  			return; +		} -		/* read cmd from tail */ +		wil_dbg_wmi(wil, "Mbox head %08x tail %08x\n", +			    r->head, r->tail); +		/* read cmd descriptor from tail */  		wil_memcpy_fromio_32(&d_tail, wil->csr + HOSTADDR(r->tail),  				     sizeof(struct wil6210_mbox_ring_desc));  		if (d_tail.sync == 0) { @@ -613,13 +687,18 @@ void wmi_recv_cmd(struct wil6210_priv *wil)  			return;  		} +		/* read cmd header from descriptor */  		if (0 != wmi_read_hdr(wil, d_tail.addr, &hdr)) {  			wil_err(wil, "Mbox evt at 0x%08x?\n",  				le32_to_cpu(d_tail.addr));  			return;  		} -  		len = le16_to_cpu(hdr.len); +		wil_dbg_wmi(wil, "Mbox evt %04x %04x %04x %02x\n", +			    le16_to_cpu(hdr.seq), len, le16_to_cpu(hdr.type), +			    hdr.flags); + +		/* read cmd buffer from descriptor */  		src = wmi_buffer(wil, d_tail.addr) +  		      sizeof(struct wil6210_mbox_hdr);  		evt = kmalloc(ALIGN(offsetof(struct pending_wmi_event, @@ -635,9 +714,6 @@ void wmi_recv_cmd(struct wil6210_priv *wil)  		iowrite32(0, wil->csr + HOSTADDR(r->tail) +  			  offsetof(struct wil6210_mbox_ring_desc, sync));  		/* indicate */ -		wil_dbg_wmi(wil, "Mbox evt %04x %04x %04x %02x\n", -			    le16_to_cpu(hdr.seq), len, le16_to_cpu(hdr.type), -			    hdr.flags);  		if ((hdr.type == WIL_MBOX_HDR_TYPE_WMI) &&  		    (len >= sizeof(struct wil6210_mbox_hdr_wmi))) {  			struct wil6210_mbox_hdr_wmi *wmi = &evt->event.wmi; @@ -667,6 +743,8 @@ void wmi_recv_cmd(struct wil6210_priv *wil)  			wil_dbg_wmi(wil, "queue_work -> %d\n", q);  		}  	} +	if (n > 1) +		wil_dbg_wmi(wil, "%s -> %d events processed\n", __func__, n);  }  int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len, @@ -735,6 +813,7 @@ int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan)  		.network_type = wmi_nettype,  		.disable_sec_offload = 1,  		.channel = chan - 1, +		.pcp_max_assoc_sta = WIL6210_MAX_CID,  	};  	struct {  		struct wil6210_mbox_hdr_wmi wmi; @@ -893,6 +972,38 @@ int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie)  	return rc;  } +/** + * wmi_rxon - turn radio on/off + * @on:		turn on if true, off otherwise + * + * Only switch radio. Channel should be set separately. + * No timeout for rxon - radio turned on forever unless some other call + * turns it off + */ +int wmi_rxon(struct wil6210_priv *wil, bool on) +{ +	int rc; +	struct { +		struct wil6210_mbox_hdr_wmi wmi; +		struct wmi_listen_started_event evt; +	} __packed reply; + +	wil_info(wil, "%s(%s)\n", __func__, on ? "on" : "off"); + +	if (on) { +		rc = wmi_call(wil, WMI_START_LISTEN_CMDID, NULL, 0, +			      WMI_LISTEN_STARTED_EVENTID, +			      &reply, sizeof(reply), 100); +		if ((rc == 0) && (reply.evt.status != WMI_FW_STATUS_SUCCESS)) +			rc = -EINVAL; +	} else { +		rc = wmi_call(wil, WMI_DISCOVERY_STOP_CMDID, NULL, 0, +			      WMI_DISCOVERY_STOPPED_EVENTID, NULL, 0, 20); +	} + +	return rc; +} +  int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring)  {  	struct wireless_dev *wdev = wil->wdev; @@ -906,6 +1017,7 @@ int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring)  		},  		.mid = 0, /* TODO - what is it? */  		.decap_trans_type = WMI_DECAP_TYPE_802_3, +		.reorder_type = WMI_RX_SW_REORDER,  	};  	struct {  		struct wil6210_mbox_hdr_wmi wmi; @@ -973,6 +1085,18 @@ int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_m, u32 *t_r)  	return 0;  } +int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac, u16 reason) +{ +	struct wmi_disconnect_sta_cmd cmd = { +		.disconnect_reason = cpu_to_le16(reason), +	}; +	memcpy(cmd.dst_mac, mac, ETH_ALEN); + +	wil_dbg_wmi(wil, "%s(%pM, reason %d)\n", __func__, mac, reason); + +	return wmi_send(wil, WMI_DISCONNECT_STA_CMDID, &cmd, sizeof(cmd)); +} +  void wmi_event_flush(struct wil6210_priv *wil)  {  	struct pending_wmi_event *evt, *t; diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h index 50b8528394f..17334c85286 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.h +++ b/drivers/net/wireless/ath/wil6210/wmi.h @@ -28,7 +28,7 @@  #define __WILOCITY_WMI_H__  /* General */ - +#define WILOCITY_MAX_ASSOC_STA (8)  #define WMI_MAC_LEN		(6)  #define WMI_PROX_RANGE_NUM	(3) @@ -219,15 +219,6 @@ struct wmi_disconnect_sta_cmd {  	__le16 disconnect_reason;  } __packed; -/* - * WMI_RECONNECT_CMDID - */ -struct wmi_reconnect_cmd { -	u8 channel;			/* hint */ -	u8 reserved; -	u8 bssid[WMI_MAC_LEN];		/* mandatory if set */ -} __packed; -  /*   * WMI_SET_PMK_CMDID @@ -296,11 +287,13 @@ enum wmi_scan_type {  	WMI_LONG_SCAN		= 0,  	WMI_SHORT_SCAN		= 1,  	WMI_PBC_SCAN		= 2, +	WMI_ACTIVE_SCAN		= 3, +	WMI_DIRECT_SCAN		= 4,  };  struct wmi_start_scan_cmd { -	u8 reserved[8]; - +	u8 direct_scan_mac_addr[6]; +	u8 reserved[2];  	__le32 home_dwell_time;	/* Max duration in the home channel(ms) */  	__le32 force_scan_interval;	/* Time interval between scans (ms)*/  	u8 scan_type;		/* wmi_scan_type */ @@ -332,6 +325,7 @@ struct wmi_probed_ssid_cmd {  	u8 ssid[WMI_MAX_SSID_LEN];  } __packed; +  /*   * WMI_SET_APPIE_CMDID   * Add Application specified IE to a management frame @@ -427,7 +421,7 @@ struct wmi_bcon_ctrl_cmd {  	__le16 frag_num;  	__le64 ss_mask;  	u8 network_type; -	u8 reserved; +	u8 pcp_max_assoc_sta;  	u8 disable_sec_offload;  	u8 disable_sec;  } __packed; @@ -450,7 +444,7 @@ enum wmi_port_role {  struct wmi_port_allocate_cmd {  	u8 mac[WMI_MAC_LEN];  	u8 port_role; -	u8 midid; +	u8 mid;  } __packed;  /* @@ -467,6 +461,7 @@ struct wmi_delete_port_cmd {  enum wmi_discovery_mode {  	WMI_DISCOVERY_MODE_NON_OFFLOAD	= 0,  	WMI_DISCOVERY_MODE_OFFLOAD	= 1, +	WMI_DISCOVERY_MODE_PEER2PEER	= 2,  };  struct wmi_p2p_cfg_cmd { @@ -493,7 +488,8 @@ struct wmi_power_mgmt_cfg_cmd {   */  struct wmi_pcp_start_cmd {  	__le16 bcon_interval; -	u8 reserved0[10]; +	u8 pcp_max_assoc_sta; +	u8 reserved0[9];  	u8 network_type;  	u8 channel;  	u8 disable_sec_offload; @@ -857,6 +853,7 @@ enum wmi_event_id {  	WMI_RF_MGMT_STATUS_EVENTID		= 0x1853,  	WMI_BF_SM_MGMT_DONE_EVENTID		= 0x1838,  	WMI_RX_MGMT_PACKET_EVENTID		= 0x1840, +	WMI_TX_MGMT_PACKET_EVENTID		= 0x1841,  	/* Performance monitoring events */  	WMI_DATA_PORT_OPEN_EVENTID		= 0x1860, @@ -1040,16 +1037,23 @@ enum wmi_disconnect_reason {  struct wmi_disconnect_event {  	__le16 protocol_reason_status;	/* reason code, see 802.11 spec. */  	u8 bssid[WMI_MAC_LEN];		/* set if known */ -	u8 disconnect_reason;		/* see wmi_disconnect_reason_e */ -	u8 assoc_resp_len; -	u8 assoc_info[0]; +	u8 disconnect_reason;		/* see wmi_disconnect_reason */ +	u8 assoc_resp_len;		/* not in use */ +	u8 assoc_info[0];		/* not in use */  } __packed;  /*   * WMI_SCAN_COMPLETE_EVENTID   */ +enum scan_status { +	WMI_SCAN_SUCCESS	= 0, +	WMI_SCAN_FAILED		= 1, +	WMI_SCAN_ABORTED	= 2, +	WMI_SCAN_REJECTED	= 3, +}; +  struct wmi_scan_complete_event { -	__le32 status; +	__le32 status;	/* scan_status */  } __packed;  /* @@ -1256,6 +1260,14 @@ struct wmi_rx_mgmt_info {  	u8 channel;	/* From Radio MNGR */  } __packed; + +/* + * WMI_TX_MGMT_PACKET_EVENTID + */ +struct wmi_tx_mgmt_packet_event { +	u8 payload[0]; +} __packed; +  struct wmi_rx_mgmt_packet_event {  	struct wmi_rx_mgmt_info info;  	u8 payload[0];  | 
