Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 18 Apr 2026 21:03:35 +0000
From:      Bjoern A. Zeeb <bz@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: 63a40b65c9be - main - mt76: update Mediatek's mt76 driver
Message-ID:  <69e3f1a7.263cf.6ebf7817@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch main has been updated by bz:

URL: https://cgit.FreeBSD.org/src/commit/?id=63a40b65c9be74193bb07a76fd66c249bd562eae

commit 63a40b65c9be74193bb07a76fd66c249bd562eae
Merge: 3780e6f9db01 5927bf24daca
Author:     Bjoern A. Zeeb <bz@FreeBSD.org>
AuthorDate: 2026-04-18 21:01:39 +0000
Commit:     Bjoern A. Zeeb <bz@FreeBSD.org>
CommitDate: 2026-04-18 21:01:39 +0000

    mt76: update Mediatek's mt76 driver
    
    This version is based on
    git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
    028ef9c96e96197026887c0f092424679298aae8 ( tag: v7.0 ).
    
    Sponsored by:   The FreeBSD Foundation
    MFC after:      3 days

 sys/contrib/dev/mediatek/mt76/agg-rx.c          | 2 +-
 sys/contrib/dev/mediatek/mt76/mt7615/mac.c      | 2 +-
 sys/contrib/dev/mediatek/mt76/mt7615/mt7615.h   | 1 +
 sys/contrib/dev/mediatek/mt76/mt76_connac_mac.c | 1 +
 sys/contrib/dev/mediatek/mt76/mt7925/mac.c      | 1 +
 sys/contrib/dev/mediatek/mt76/mt7996/mac.c      | 1 +
 sys/contrib/dev/mediatek/mt76/mt7996/main.c     | 2 +-
 sys/contrib/dev/mediatek/mt76/mt7996/mcu.c      | 2 +-
 sys/contrib/dev/mediatek/mt76/scan.c            | 4 +---
 9 files changed, 9 insertions(+), 7 deletions(-)

diff --cc sys/contrib/dev/mediatek/mt76/mt7615/mac.c
index a4a252dc0186,000000000000..0ba8a0a6fe8f
mode 100644,000000..100644
--- a/sys/contrib/dev/mediatek/mt76/mt7615/mac.c
+++ b/sys/contrib/dev/mediatek/mt76/mt7615/mac.c
@@@ -1,2371 -1,0 +1,2371 @@@
 +// SPDX-License-Identifier: BSD-3-Clause-Clear
 +/* Copyright (C) 2019 MediaTek Inc.
 + *
 + * Author: Ryder Lee <ryder.lee@mediatek.com>
 + *         Roy Luo <royluo@google.com>
 + *         Felix Fietkau <nbd@nbd.name>
 + *         Lorenzo Bianconi <lorenzo@kernel.org>
 + */
 +
 +#include <linux/devcoredump.h>
 +#include <linux/etherdevice.h>
 +#include <linux/timekeeping.h>
 +#if defined(__FreeBSD__)
 +#include <linux/delay.h>
 +#endif
 +#include "mt7615.h"
 +#include "../trace.h"
 +#include "../dma.h"
 +#include "mt7615_trace.h"
 +#include "mac.h"
 +#include "mcu.h"
 +
 +#define to_rssi(field, rxv)		((FIELD_GET(field, rxv) - 220) / 2)
 +
 +static const struct mt7615_dfs_radar_spec etsi_radar_specs = {
 +	.pulse_th = { 110, -10, -80, 40, 5200, 128, 5200 },
 +	.radar_pattern = {
 +		[5] =  { 1, 0,  6, 32, 28, 0, 17,  990, 5010, 1, 1 },
 +		[6] =  { 1, 0,  9, 32, 28, 0, 27,  615, 5010, 1, 1 },
 +		[7] =  { 1, 0, 15, 32, 28, 0, 27,  240,  445, 1, 1 },
 +		[8] =  { 1, 0, 12, 32, 28, 0, 42,  240,  510, 1, 1 },
 +		[9] =  { 1, 1,  0,  0,  0, 0, 14, 2490, 3343, 0, 0, 12, 32, 28 },
 +		[10] = { 1, 1,  0,  0,  0, 0, 14, 2490, 3343, 0, 0, 15, 32, 24 },
 +		[11] = { 1, 1,  0,  0,  0, 0, 14,  823, 2510, 0, 0, 18, 32, 28 },
 +		[12] = { 1, 1,  0,  0,  0, 0, 14,  823, 2510, 0, 0, 27, 32, 24 },
 +	},
 +};
 +
 +static const struct mt7615_dfs_radar_spec fcc_radar_specs = {
 +	.pulse_th = { 110, -10, -80, 40, 5200, 128, 5200 },
 +	.radar_pattern = {
 +		[0] = { 1, 0,  9,  32, 28, 0, 13, 508, 3076, 1,  1 },
 +		[1] = { 1, 0, 12,  32, 28, 0, 17, 140,  240, 1,  1 },
 +		[2] = { 1, 0,  8,  32, 28, 0, 22, 190,  510, 1,  1 },
 +		[3] = { 1, 0,  6,  32, 28, 0, 32, 190,  510, 1,  1 },
 +		[4] = { 1, 0,  9, 255, 28, 0, 13, 323,  343, 1, 32 },
 +	},
 +};
 +
 +static const struct mt7615_dfs_radar_spec jp_radar_specs = {
 +	.pulse_th = { 110, -10, -80, 40, 5200, 128, 5200 },
 +	.radar_pattern = {
 +		[0] =  { 1, 0,  8, 32, 28, 0, 13,  508, 3076, 1,  1 },
 +		[1] =  { 1, 0, 12, 32, 28, 0, 17,  140,  240, 1,  1 },
 +		[2] =  { 1, 0,  8, 32, 28, 0, 22,  190,  510, 1,  1 },
 +		[3] =  { 1, 0,  6, 32, 28, 0, 32,  190,  510, 1,  1 },
 +		[4] =  { 1, 0,  9, 32, 28, 0, 13,  323,  343, 1, 32 },
 +		[13] = { 1, 0, 8,  32, 28, 0, 14, 3836, 3856, 1,  1 },
 +		[14] = { 1, 0, 8,  32, 28, 0, 14, 3990, 4010, 1,  1 },
 +	},
 +};
 +
 +static enum mt76_cipher_type
 +mt7615_mac_get_cipher(int cipher)
 +{
 +	switch (cipher) {
 +	case WLAN_CIPHER_SUITE_WEP40:
 +		return MT_CIPHER_WEP40;
 +	case WLAN_CIPHER_SUITE_WEP104:
 +		return MT_CIPHER_WEP104;
 +	case WLAN_CIPHER_SUITE_TKIP:
 +		return MT_CIPHER_TKIP;
 +	case WLAN_CIPHER_SUITE_AES_CMAC:
 +		return MT_CIPHER_BIP_CMAC_128;
 +	case WLAN_CIPHER_SUITE_CCMP:
 +		return MT_CIPHER_AES_CCMP;
 +	case WLAN_CIPHER_SUITE_CCMP_256:
 +		return MT_CIPHER_CCMP_256;
 +	case WLAN_CIPHER_SUITE_GCMP:
 +		return MT_CIPHER_GCMP;
 +	case WLAN_CIPHER_SUITE_GCMP_256:
 +		return MT_CIPHER_GCMP_256;
 +	case WLAN_CIPHER_SUITE_SMS4:
 +		return MT_CIPHER_WAPI;
 +	default:
 +		return MT_CIPHER_NONE;
 +	}
 +}
 +
 +static struct mt76_wcid *mt7615_rx_get_wcid(struct mt7615_dev *dev,
 +					    u8 idx, bool unicast)
 +{
 +	struct mt7615_sta *sta;
 +	struct mt76_wcid *wcid;
 +
 +	wcid = mt76_wcid_ptr(dev, idx);
 +	if (unicast || !wcid)
 +		return wcid;
 +
 +	if (!wcid->sta)
 +		return NULL;
 +
 +	sta = container_of(wcid, struct mt7615_sta, wcid);
 +	if (!sta->vif)
 +		return NULL;
 +
 +	return &sta->vif->sta.wcid;
 +}
 +
 +void mt7615_mac_reset_counters(struct mt7615_phy *phy)
 +{
 +	struct mt7615_dev *dev = phy->dev;
 +	int i;
 +
 +	for (i = 0; i < 4; i++) {
 +		mt76_rr(dev, MT_TX_AGG_CNT(0, i));
 +		mt76_rr(dev, MT_TX_AGG_CNT(1, i));
 +	}
 +
 +	memset(phy->mt76->aggr_stats, 0, sizeof(phy->mt76->aggr_stats));
 +	phy->mt76->survey_time = ktime_get_boottime();
 +
 +	/* reset airtime counters */
 +	mt76_rr(dev, MT_MIB_SDR9(0));
 +	mt76_rr(dev, MT_MIB_SDR9(1));
 +
 +	mt76_rr(dev, MT_MIB_SDR36(0));
 +	mt76_rr(dev, MT_MIB_SDR36(1));
 +
 +	mt76_rr(dev, MT_MIB_SDR37(0));
 +	mt76_rr(dev, MT_MIB_SDR37(1));
 +
 +	mt76_set(dev, MT_WF_RMAC_MIB_TIME0, MT_WF_RMAC_MIB_RXTIME_CLR);
 +	mt76_set(dev, MT_WF_RMAC_MIB_AIRTIME0, MT_WF_RMAC_MIB_RXTIME_CLR);
 +}
 +
 +void mt7615_mac_set_timing(struct mt7615_phy *phy)
 +{
 +	s16 coverage_class = phy->coverage_class;
 +	struct mt7615_dev *dev = phy->dev;
 +	bool ext_phy = phy != &dev->phy;
 +	u32 val, reg_offset;
 +	u32 cck = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, 231) |
 +		  FIELD_PREP(MT_TIMEOUT_VAL_CCA, 48);
 +	u32 ofdm = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, 60) |
 +		   FIELD_PREP(MT_TIMEOUT_VAL_CCA, 28);
 +	int sifs, offset;
 +	bool is_5ghz = phy->mt76->chandef.chan->band == NL80211_BAND_5GHZ;
 +
 +	if (!test_bit(MT76_STATE_RUNNING, &phy->mt76->state))
 +		return;
 +
 +	if (is_5ghz)
 +		sifs = 16;
 +	else
 +		sifs = 10;
 +
 +	if (ext_phy) {
 +		coverage_class = max_t(s16, dev->phy.coverage_class,
 +				       coverage_class);
 +		mt76_set(dev, MT_ARB_SCR,
 +			 MT_ARB_SCR_TX1_DISABLE | MT_ARB_SCR_RX1_DISABLE);
 +	} else {
 +		struct mt7615_phy *phy_ext = mt7615_ext_phy(dev);
 +
 +		if (phy_ext)
 +			coverage_class = max_t(s16, phy_ext->coverage_class,
 +					       coverage_class);
 +		mt76_set(dev, MT_ARB_SCR,
 +			 MT_ARB_SCR_TX0_DISABLE | MT_ARB_SCR_RX0_DISABLE);
 +	}
 +	udelay(1);
 +
 +	offset = 3 * coverage_class;
 +	reg_offset = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, offset) |
 +		     FIELD_PREP(MT_TIMEOUT_VAL_CCA, offset);
 +	mt76_wr(dev, MT_TMAC_CDTR, cck + reg_offset);
 +	mt76_wr(dev, MT_TMAC_ODTR, ofdm + reg_offset);
 +
 +	mt76_wr(dev, MT_TMAC_ICR(ext_phy),
 +		FIELD_PREP(MT_IFS_EIFS, 360) |
 +		FIELD_PREP(MT_IFS_RIFS, 2) |
 +		FIELD_PREP(MT_IFS_SIFS, sifs) |
 +		FIELD_PREP(MT_IFS_SLOT, phy->slottime));
 +
 +	if (phy->slottime < 20 || is_5ghz)
 +		val = MT7615_CFEND_RATE_DEFAULT;
 +	else
 +		val = MT7615_CFEND_RATE_11B;
 +
 +	mt76_rmw_field(dev, MT_AGG_ACR(ext_phy), MT_AGG_ACR_CFEND_RATE, val);
 +	if (ext_phy)
 +		mt76_clear(dev, MT_ARB_SCR,
 +			   MT_ARB_SCR_TX1_DISABLE | MT_ARB_SCR_RX1_DISABLE);
 +	else
 +		mt76_clear(dev, MT_ARB_SCR,
 +			   MT_ARB_SCR_TX0_DISABLE | MT_ARB_SCR_RX0_DISABLE);
 +
 +}
 +
 +static void
 +mt7615_get_status_freq_info(struct mt7615_dev *dev, struct mt76_phy *mphy,
 +			    struct mt76_rx_status *status, u8 chfreq)
 +{
 +	if (!test_bit(MT76_HW_SCANNING, &mphy->state) &&
 +	    !test_bit(MT76_HW_SCHED_SCANNING, &mphy->state) &&
 +	    !test_bit(MT76_STATE_ROC, &mphy->state)) {
 +		status->freq = mphy->chandef.chan->center_freq;
 +		status->band = mphy->chandef.chan->band;
 +		return;
 +	}
 +
 +	status->band = chfreq <= 14 ? NL80211_BAND_2GHZ : NL80211_BAND_5GHZ;
 +	status->freq = ieee80211_channel_to_frequency(chfreq, status->band);
 +}
 +
 +static void mt7615_mac_fill_tm_rx(struct mt7615_phy *phy, __le32 *rxv)
 +{
 +#ifdef CONFIG_NL80211_TESTMODE
 +	u32 rxv1 = le32_to_cpu(rxv[0]);
 +	u32 rxv3 = le32_to_cpu(rxv[2]);
 +	u32 rxv4 = le32_to_cpu(rxv[3]);
 +	u32 rxv5 = le32_to_cpu(rxv[4]);
 +	u8 cbw = FIELD_GET(MT_RXV1_FRAME_MODE, rxv1);
 +	u8 mode = FIELD_GET(MT_RXV1_TX_MODE, rxv1);
 +	s16 foe = FIELD_GET(MT_RXV5_FOE, rxv5);
 +	u32 foe_const = (BIT(cbw + 1) & 0xf) * 10000;
 +
 +	if (!mode) {
 +		/* CCK */
 +		foe &= ~BIT(11);
 +		foe *= 1000;
 +		foe >>= 11;
 +	} else {
 +		if (foe > 2048)
 +			foe -= 4096;
 +
 +		foe = (foe * foe_const) >> 15;
 +	}
 +
 +	phy->test.last_freq_offset = foe;
 +	phy->test.last_rcpi[0] = FIELD_GET(MT_RXV4_RCPI0, rxv4);
 +	phy->test.last_rcpi[1] = FIELD_GET(MT_RXV4_RCPI1, rxv4);
 +	phy->test.last_rcpi[2] = FIELD_GET(MT_RXV4_RCPI2, rxv4);
 +	phy->test.last_rcpi[3] = FIELD_GET(MT_RXV4_RCPI3, rxv4);
 +	phy->test.last_ib_rssi[0] = FIELD_GET(MT_RXV3_IB_RSSI, rxv3);
 +	phy->test.last_wb_rssi[0] = FIELD_GET(MT_RXV3_WB_RSSI, rxv3);
 +#endif
 +}
 +
 +/* The HW does not translate the mac header to 802.3 for mesh point */
 +static int mt7615_reverse_frag0_hdr_trans(struct sk_buff *skb, u16 hdr_gap)
 +{
 +	struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
 +	struct ethhdr *eth_hdr = (struct ethhdr *)(skb->data + hdr_gap);
 +	struct mt7615_sta *msta = (struct mt7615_sta *)status->wcid;
 +	__le32 *rxd = (__le32 *)skb->data;
 +	struct ieee80211_sta *sta;
 +	struct ieee80211_vif *vif;
 +	struct ieee80211_hdr hdr;
 +	u16 frame_control;
 +
 +	if (le32_get_bits(rxd[1], MT_RXD1_NORMAL_ADDR_TYPE) !=
 +	    MT_RXD1_NORMAL_U2M)
 +		return -EINVAL;
 +
 +	if (!(le32_to_cpu(rxd[0]) & MT_RXD0_NORMAL_GROUP_4))
 +		return -EINVAL;
 +
 +	if (!msta || !msta->vif)
 +		return -EINVAL;
 +
 +	sta = container_of((void *)msta, struct ieee80211_sta, drv_priv);
 +	vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv);
 +
 +	/* store the info from RXD and ethhdr to avoid being overridden */
 +	frame_control = le32_get_bits(rxd[4], MT_RXD4_FRAME_CONTROL);
 +	hdr.frame_control = cpu_to_le16(frame_control);
 +	hdr.seq_ctrl = cpu_to_le16(le32_get_bits(rxd[6], MT_RXD6_SEQ_CTRL));
 +	hdr.duration_id = 0;
 +
 +	ether_addr_copy(hdr.addr1, vif->addr);
 +	ether_addr_copy(hdr.addr2, sta->addr);
 +	switch (frame_control & (IEEE80211_FCTL_TODS |
 +				 IEEE80211_FCTL_FROMDS)) {
 +	case 0:
 +		ether_addr_copy(hdr.addr3, vif->bss_conf.bssid);
 +		break;
 +	case IEEE80211_FCTL_FROMDS:
 +		ether_addr_copy(hdr.addr3, eth_hdr->h_source);
 +		break;
 +	case IEEE80211_FCTL_TODS:
 +		ether_addr_copy(hdr.addr3, eth_hdr->h_dest);
 +		break;
 +	case IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS:
 +		ether_addr_copy(hdr.addr3, eth_hdr->h_dest);
 +		ether_addr_copy(hdr.addr4, eth_hdr->h_source);
 +		break;
 +	default:
 +		break;
 +	}
 +
 +	skb_pull(skb, hdr_gap + sizeof(struct ethhdr) - 2);
 +	if (eth_hdr->h_proto == cpu_to_be16(ETH_P_AARP) ||
 +	    eth_hdr->h_proto == cpu_to_be16(ETH_P_IPX))
 +		ether_addr_copy(skb_push(skb, ETH_ALEN), bridge_tunnel_header);
 +	else if (be16_to_cpu(eth_hdr->h_proto) >= ETH_P_802_3_MIN)
 +		ether_addr_copy(skb_push(skb, ETH_ALEN), rfc1042_header);
 +	else
 +		skb_pull(skb, 2);
 +
 +	if (ieee80211_has_order(hdr.frame_control))
 +		memcpy(skb_push(skb, IEEE80211_HT_CTL_LEN), &rxd[7],
 +		       IEEE80211_HT_CTL_LEN);
 +
 +	if (ieee80211_is_data_qos(hdr.frame_control)) {
 +		__le16 qos_ctrl;
 +
 +		qos_ctrl = cpu_to_le16(le32_get_bits(rxd[6], MT_RXD6_QOS_CTL));
 +		memcpy(skb_push(skb, IEEE80211_QOS_CTL_LEN), &qos_ctrl,
 +		       IEEE80211_QOS_CTL_LEN);
 +	}
 +
 +	if (ieee80211_has_a4(hdr.frame_control))
 +		memcpy(skb_push(skb, sizeof(hdr)), &hdr, sizeof(hdr));
 +	else
 +		memcpy(skb_push(skb, sizeof(hdr) - 6), &hdr, sizeof(hdr) - 6);
 +
 +	status->flag &= ~(RX_FLAG_RADIOTAP_HE | RX_FLAG_RADIOTAP_HE_MU);
 +	return 0;
 +}
 +
 +static int mt7615_mac_fill_rx(struct mt7615_dev *dev, struct sk_buff *skb)
 +{
 +	struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
 +	struct mt76_phy *mphy = &dev->mt76.phy;
 +	struct mt7615_phy *phy = &dev->phy;
 +	struct ieee80211_supported_band *sband;
 +	struct ieee80211_hdr *hdr;
 +	struct mt7615_phy *phy2;
 +	__le32 *rxd = (__le32 *)skb->data;
 +	u32 rxd0 = le32_to_cpu(rxd[0]);
 +	u32 rxd1 = le32_to_cpu(rxd[1]);
 +	u32 rxd2 = le32_to_cpu(rxd[2]);
 +	u32 csum_mask = MT_RXD0_NORMAL_IP_SUM | MT_RXD0_NORMAL_UDP_TCP_SUM;
 +	u32 csum_status = *(u32 *)skb->cb;
 +	bool unicast, hdr_trans, remove_pad, insert_ccmp_hdr = false;
 +	u16 hdr_gap;
 +	int phy_idx;
 +	int i, idx;
 +	u8 chfreq, amsdu_info, qos_ctl = 0;
 +	u16 seq_ctrl = 0;
 +	__le16 fc = 0;
 +
 +	memset(status, 0, sizeof(*status));
 +
 +	chfreq = FIELD_GET(MT_RXD1_NORMAL_CH_FREQ, rxd1);
 +
 +	phy2 = dev->mt76.phys[MT_BAND1] ? dev->mt76.phys[MT_BAND1]->priv : NULL;
 +	if (!phy2)
 +		phy_idx = 0;
 +	else if (phy2->chfreq == phy->chfreq)
 +		phy_idx = -1;
 +	else if (phy->chfreq == chfreq)
 +		phy_idx = 0;
 +	else if (phy2->chfreq == chfreq)
 +		phy_idx = 1;
 +	else
 +		phy_idx = -1;
 +
 +	if (rxd2 & MT_RXD2_NORMAL_AMSDU_ERR)
 +		return -EINVAL;
 +
 +	hdr_trans = rxd1 & MT_RXD1_NORMAL_HDR_TRANS;
 +	if (hdr_trans && (rxd2 & MT_RXD2_NORMAL_CM))
 +		return -EINVAL;
 +
 +	/* ICV error or CCMP/BIP/WPI MIC error */
 +	if (rxd2 & MT_RXD2_NORMAL_ICV_ERR)
 +		status->flag |= RX_FLAG_ONLY_MONITOR;
 +
 +	unicast = (rxd1 & MT_RXD1_NORMAL_ADDR_TYPE) == MT_RXD1_NORMAL_U2M;
 +	idx = FIELD_GET(MT_RXD2_NORMAL_WLAN_IDX, rxd2);
 +	status->wcid = mt7615_rx_get_wcid(dev, idx, unicast);
 +
 +	if (status->wcid) {
 +		struct mt7615_sta *msta;
 +
 +		msta = container_of(status->wcid, struct mt7615_sta, wcid);
 +		mt76_wcid_add_poll(&dev->mt76, &msta->wcid);
 +	}
 +
 +	if (mt76_is_mmio(&dev->mt76) && (rxd0 & csum_mask) == csum_mask &&
 +	    !(csum_status & (BIT(0) | BIT(2) | BIT(3))))
 +		skb->ip_summed = CHECKSUM_UNNECESSARY;
 +
 +	if (rxd2 & MT_RXD2_NORMAL_FCS_ERR)
 +		status->flag |= RX_FLAG_FAILED_FCS_CRC;
 +
 +	if (rxd2 & MT_RXD2_NORMAL_TKIP_MIC_ERR)
 +		status->flag |= RX_FLAG_MMIC_ERROR;
 +
 +	if (FIELD_GET(MT_RXD2_NORMAL_SEC_MODE, rxd2) != 0 &&
 +	    !(rxd2 & (MT_RXD2_NORMAL_CLM | MT_RXD2_NORMAL_CM))) {
 +		status->flag |= RX_FLAG_DECRYPTED;
 +		status->flag |= RX_FLAG_IV_STRIPPED;
 +		status->flag |= RX_FLAG_MMIC_STRIPPED | RX_FLAG_MIC_STRIPPED;
 +	}
 +
 +	remove_pad = rxd1 & MT_RXD1_NORMAL_HDR_OFFSET;
 +
 +	if (rxd2 & MT_RXD2_NORMAL_MAX_LEN_ERROR)
 +		return -EINVAL;
 +
 +	rxd += 4;
 +	if (rxd0 & MT_RXD0_NORMAL_GROUP_4) {
 +		u32 v0 = le32_to_cpu(rxd[0]);
 +		u32 v2 = le32_to_cpu(rxd[2]);
 +
 +		fc = cpu_to_le16(FIELD_GET(MT_RXD4_FRAME_CONTROL, v0));
 +		qos_ctl = FIELD_GET(MT_RXD6_QOS_CTL, v2);
 +		seq_ctrl = FIELD_GET(MT_RXD6_SEQ_CTRL, v2);
 +
 +		rxd += 4;
 +		if ((u8 *)rxd - skb->data >= skb->len)
 +			return -EINVAL;
 +	}
 +
 +	if (rxd0 & MT_RXD0_NORMAL_GROUP_1) {
 +		u8 *data = (u8 *)rxd;
 +
 +		if (status->flag & RX_FLAG_DECRYPTED) {
 +			switch (FIELD_GET(MT_RXD2_NORMAL_SEC_MODE, rxd2)) {
 +			case MT_CIPHER_AES_CCMP:
 +			case MT_CIPHER_CCMP_CCX:
 +			case MT_CIPHER_CCMP_256:
 +				insert_ccmp_hdr =
 +					FIELD_GET(MT_RXD2_NORMAL_FRAG, rxd2);
 +				fallthrough;
 +			case MT_CIPHER_TKIP:
 +			case MT_CIPHER_TKIP_NO_MIC:
 +			case MT_CIPHER_GCMP:
 +			case MT_CIPHER_GCMP_256:
 +				status->iv[0] = data[5];
 +				status->iv[1] = data[4];
 +				status->iv[2] = data[3];
 +				status->iv[3] = data[2];
 +				status->iv[4] = data[1];
 +				status->iv[5] = data[0];
 +				break;
 +			default:
 +				break;
 +			}
 +		}
 +		rxd += 4;
 +		if ((u8 *)rxd - skb->data >= skb->len)
 +			return -EINVAL;
 +	}
 +
 +	if (rxd0 & MT_RXD0_NORMAL_GROUP_2) {
 +		status->timestamp = le32_to_cpu(rxd[0]);
 +		status->flag |= RX_FLAG_MACTIME_START;
 +
 +		if (!(rxd2 & (MT_RXD2_NORMAL_NON_AMPDU_SUB |
 +			      MT_RXD2_NORMAL_NON_AMPDU))) {
 +			status->flag |= RX_FLAG_AMPDU_DETAILS;
 +
 +			/* all subframes of an A-MPDU have the same timestamp */
 +			if (phy->rx_ampdu_ts != status->timestamp) {
 +				if (!++phy->ampdu_ref)
 +					phy->ampdu_ref++;
 +			}
 +			phy->rx_ampdu_ts = status->timestamp;
 +
 +			status->ampdu_ref = phy->ampdu_ref;
 +		}
 +
 +		rxd += 2;
 +		if ((u8 *)rxd - skb->data >= skb->len)
 +			return -EINVAL;
 +	}
 +
 +	if (rxd0 & MT_RXD0_NORMAL_GROUP_3) {
 +		u32 rxdg5 = le32_to_cpu(rxd[5]);
 +
 +		/*
 +		 * If both PHYs are on the same channel and we don't have a WCID,
 +		 * we need to figure out which PHY this packet was received on.
 +		 * On the primary PHY, the noise value for the chains belonging to the
 +		 * second PHY will be set to the noise value of the last packet from
 +		 * that PHY.
 +		 */
 +		if (phy_idx < 0) {
 +			int first_chain = ffs(phy2->mt76->chainmask) - 1;
 +
 +			phy_idx = ((rxdg5 >> (first_chain * 8)) & 0xff) == 0;
 +		}
 +	}
 +
 +	if (phy_idx == 1 && phy2) {
 +		mphy = dev->mt76.phys[MT_BAND1];
 +		phy = phy2;
 +		status->phy_idx = phy_idx;
 +	}
 +
 +	if (!mt7615_firmware_offload(dev) && chfreq != phy->chfreq)
 +		return -EINVAL;
 +
 +	mt7615_get_status_freq_info(dev, mphy, status, chfreq);
 +	if (status->band == NL80211_BAND_5GHZ)
 +		sband = &mphy->sband_5g.sband;
 +	else
 +		sband = &mphy->sband_2g.sband;
 +
 +	if (!test_bit(MT76_STATE_RUNNING, &mphy->state))
 +		return -EINVAL;
 +
 +	if (!sband->channels)
 +		return -EINVAL;
 +
 +	if (rxd0 & MT_RXD0_NORMAL_GROUP_3) {
 +		u32 rxdg0 = le32_to_cpu(rxd[0]);
 +		u32 rxdg1 = le32_to_cpu(rxd[1]);
 +		u32 rxdg3 = le32_to_cpu(rxd[3]);
 +		u8 stbc = FIELD_GET(MT_RXV1_HT_STBC, rxdg0);
 +		bool cck = false;
 +
 +		i = FIELD_GET(MT_RXV1_TX_RATE, rxdg0);
 +		switch (FIELD_GET(MT_RXV1_TX_MODE, rxdg0)) {
 +		case MT_PHY_TYPE_CCK:
 +			cck = true;
 +			fallthrough;
 +		case MT_PHY_TYPE_OFDM:
 +			i = mt76_get_rate(&dev->mt76, sband, i, cck);
 +			break;
 +		case MT_PHY_TYPE_HT_GF:
 +		case MT_PHY_TYPE_HT:
 +			status->encoding = RX_ENC_HT;
 +			if (i > 31)
 +				return -EINVAL;
 +			break;
 +		case MT_PHY_TYPE_VHT:
 +			status->nss = FIELD_GET(MT_RXV2_NSTS, rxdg1) + 1;
 +			status->encoding = RX_ENC_VHT;
 +			break;
 +		default:
 +			return -EINVAL;
 +		}
 +		status->rate_idx = i;
 +
 +		switch (FIELD_GET(MT_RXV1_FRAME_MODE, rxdg0)) {
 +		case MT_PHY_BW_20:
 +			break;
 +		case MT_PHY_BW_40:
 +			status->bw = RATE_INFO_BW_40;
 +			break;
 +		case MT_PHY_BW_80:
 +			status->bw = RATE_INFO_BW_80;
 +			break;
 +		case MT_PHY_BW_160:
 +			status->bw = RATE_INFO_BW_160;
 +			break;
 +		default:
 +			return -EINVAL;
 +		}
 +
 +		if (rxdg0 & MT_RXV1_HT_SHORT_GI)
 +			status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
 +		if (rxdg0 & MT_RXV1_HT_AD_CODE)
 +			status->enc_flags |= RX_ENC_FLAG_LDPC;
 +
 +		status->enc_flags |= RX_ENC_FLAG_STBC_MASK * stbc;
 +
 +		status->chains = mphy->antenna_mask;
 +		status->chain_signal[0] = to_rssi(MT_RXV4_RCPI0, rxdg3);
 +		status->chain_signal[1] = to_rssi(MT_RXV4_RCPI1, rxdg3);
 +		status->chain_signal[2] = to_rssi(MT_RXV4_RCPI2, rxdg3);
 +		status->chain_signal[3] = to_rssi(MT_RXV4_RCPI3, rxdg3);
 +
 +		mt7615_mac_fill_tm_rx(mphy->priv, rxd);
 +
 +		rxd += 6;
 +		if ((u8 *)rxd - skb->data >= skb->len)
 +			return -EINVAL;
 +	}
 +
 +	amsdu_info = FIELD_GET(MT_RXD1_NORMAL_PAYLOAD_FORMAT, rxd1);
 +	status->amsdu = !!amsdu_info;
 +	if (status->amsdu) {
 +		status->first_amsdu = amsdu_info == MT_RXD1_FIRST_AMSDU_FRAME;
 +		status->last_amsdu = amsdu_info == MT_RXD1_LAST_AMSDU_FRAME;
 +	}
 +
 +	hdr_gap = (u8 *)rxd - skb->data + 2 * remove_pad;
 +	if (hdr_trans && ieee80211_has_morefrags(fc)) {
 +		if (mt7615_reverse_frag0_hdr_trans(skb, hdr_gap))
 +			return -EINVAL;
 +		hdr_trans = false;
 +	} else {
 +		int pad_start = 0;
 +
 +		skb_pull(skb, hdr_gap);
 +		if (!hdr_trans && status->amsdu) {
 +			pad_start = ieee80211_get_hdrlen_from_skb(skb);
 +		} else if (hdr_trans && (rxd2 & MT_RXD2_NORMAL_HDR_TRANS_ERROR)) {
 +			/*
 +			 * When header translation failure is indicated,
 +			 * the hardware will insert an extra 2-byte field
 +			 * containing the data length after the protocol
 +			 * type field. This happens either when the LLC-SNAP
 +			 * pattern did not match, or if a VLAN header was
 +			 * detected.
 +			 */
 +			pad_start = 12;
 +			if (get_unaligned_be16(skb->data + pad_start) == ETH_P_8021Q)
 +				pad_start += 4;
 +			else
 +				pad_start = 0;
 +		}
 +
 +		if (pad_start) {
 +			memmove(skb->data + 2, skb->data, pad_start);
 +			skb_pull(skb, 2);
 +		}
 +	}
 +
 +	if (insert_ccmp_hdr && !hdr_trans) {
 +		u8 key_id = FIELD_GET(MT_RXD1_NORMAL_KEY_ID, rxd1);
 +
 +		mt76_insert_ccmp_hdr(skb, key_id);
 +	}
 +
 +	if (!hdr_trans) {
 +		hdr = (struct ieee80211_hdr *)skb->data;
 +		fc = hdr->frame_control;
 +		if (ieee80211_is_data_qos(fc)) {
 +			seq_ctrl = le16_to_cpu(hdr->seq_ctrl);
 +			qos_ctl = *ieee80211_get_qos_ctl(hdr);
 +		}
 +	} else {
 +		status->flag |= RX_FLAG_8023;
 +	}
 +
 +	if (!status->wcid || !ieee80211_is_data_qos(fc))
 +		return 0;
 +
 +	status->aggr = unicast &&
 +		       !ieee80211_is_qos_nullfunc(fc);
 +	status->qos_ctl = qos_ctl;
 +	status->seqno = IEEE80211_SEQ_TO_SN(seq_ctrl);
 +
 +	return 0;
 +}
 +
 +static u16
 +mt7615_mac_tx_rate_val(struct mt7615_dev *dev,
 +		       struct mt76_phy *mphy,
 +		       const struct ieee80211_tx_rate *rate,
 +		       bool stbc, u8 *bw)
 +{
 +	u8 phy, nss, rate_idx;
 +	u16 rateval = 0;
 +
 +	*bw = 0;
 +
 +	if (rate->flags & IEEE80211_TX_RC_VHT_MCS) {
 +		rate_idx = ieee80211_rate_get_vht_mcs(rate);
 +		nss = ieee80211_rate_get_vht_nss(rate);
 +		phy = MT_PHY_TYPE_VHT;
 +		if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
 +			*bw = 1;
 +		else if (rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH)
 +			*bw = 2;
 +		else if (rate->flags & IEEE80211_TX_RC_160_MHZ_WIDTH)
 +			*bw = 3;
 +	} else if (rate->flags & IEEE80211_TX_RC_MCS) {
 +		rate_idx = rate->idx;
 +		nss = 1 + (rate->idx >> 3);
 +		phy = MT_PHY_TYPE_HT;
 +		if (rate->flags & IEEE80211_TX_RC_GREEN_FIELD)
 +			phy = MT_PHY_TYPE_HT_GF;
 +		if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
 +			*bw = 1;
 +	} else {
 +		const struct ieee80211_rate *r;
 +		int band = mphy->chandef.chan->band;
 +		u16 val;
 +
 +		nss = 1;
 +		r = &mphy->hw->wiphy->bands[band]->bitrates[rate->idx];
 +		if (rate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE)
 +			val = r->hw_value_short;
 +		else
 +			val = r->hw_value;
 +
 +		phy = val >> 8;
 +		rate_idx = val & 0xff;
 +	}
 +
 +	if (stbc && nss == 1) {
 +		nss++;
 +		rateval |= MT_TX_RATE_STBC;
 +	}
 +
 +	rateval |= (FIELD_PREP(MT_TX_RATE_IDX, rate_idx) |
 +		    FIELD_PREP(MT_TX_RATE_MODE, phy) |
 +		    FIELD_PREP(MT_TX_RATE_NSS, nss - 1));
 +
 +	return rateval;
 +}
 +
 +int mt7615_mac_write_txwi(struct mt7615_dev *dev, __le32 *txwi,
 +			  struct sk_buff *skb, struct mt76_wcid *wcid,
 +			  struct ieee80211_sta *sta, int pid,
 +			  struct ieee80211_key_conf *key,
 +			  enum mt76_txq_id qid, bool beacon)
 +{
 +	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 +	u8 fc_type, fc_stype, p_fmt, q_idx, omac_idx = 0, wmm_idx = 0;
 +	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 +	struct ieee80211_tx_rate *rate = &info->control.rates[0];
 +	u8 phy_idx = (info->hw_queue & MT_TX_HW_QUEUE_PHY) >> 2;
 +	bool multicast = is_multicast_ether_addr(hdr->addr1);
 +	struct ieee80211_vif *vif = info->control.vif;
 +	bool is_mmio = mt76_is_mmio(&dev->mt76);
 +	u32 val, sz_txd = is_mmio ? MT_TXD_SIZE : MT_USB_TXD_SIZE;
 +	struct mt76_phy *mphy = &dev->mphy;
 +	__le16 fc = hdr->frame_control;
 +	int tx_count = 8;
 +	u16 seqno = 0;
 +
 +	if (vif) {
 +		struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv;
 +
 +		omac_idx = mvif->omac_idx;
 +		wmm_idx = mvif->wmm_idx;
 +	}
 +
 +	if (sta) {
 +		struct mt7615_sta *msta = (struct mt7615_sta *)sta->drv_priv;
 +
 +		tx_count = msta->rate_count;
 +	}
 +
 +	if (phy_idx && dev->mt76.phys[MT_BAND1])
 +		mphy = dev->mt76.phys[MT_BAND1];
 +
 +	fc_type = (le16_to_cpu(fc) & IEEE80211_FCTL_FTYPE) >> 2;
 +	fc_stype = (le16_to_cpu(fc) & IEEE80211_FCTL_STYPE) >> 4;
 +
 +	if (beacon) {
 +		p_fmt = MT_TX_TYPE_FW;
 +		q_idx = phy_idx ? MT_LMAC_BCN1 : MT_LMAC_BCN0;
 +	} else if (qid >= MT_TXQ_PSD) {
 +		p_fmt = is_mmio ? MT_TX_TYPE_CT : MT_TX_TYPE_SF;
 +		q_idx = phy_idx ? MT_LMAC_ALTX1 : MT_LMAC_ALTX0;
 +	} else {
 +		p_fmt = is_mmio ? MT_TX_TYPE_CT : MT_TX_TYPE_SF;
 +		q_idx = wmm_idx * MT7615_MAX_WMM_SETS +
 +			mt7615_lmac_mapping(dev, skb_get_queue_mapping(skb));
 +	}
 +
 +	val = FIELD_PREP(MT_TXD0_TX_BYTES, skb->len + sz_txd) |
 +	      FIELD_PREP(MT_TXD0_P_IDX, MT_TX_PORT_IDX_LMAC) |
 +	      FIELD_PREP(MT_TXD0_Q_IDX, q_idx);
 +	txwi[0] = cpu_to_le32(val);
 +
 +	val = MT_TXD1_LONG_FORMAT |
 +	      FIELD_PREP(MT_TXD1_WLAN_IDX, wcid->idx) |
 +	      FIELD_PREP(MT_TXD1_HDR_FORMAT, MT_HDR_FORMAT_802_11) |
 +	      FIELD_PREP(MT_TXD1_HDR_INFO,
 +			 ieee80211_get_hdrlen_from_skb(skb) / 2) |
 +	      FIELD_PREP(MT_TXD1_TID,
 +			 skb->priority & IEEE80211_QOS_CTL_TID_MASK) |
 +	      FIELD_PREP(MT_TXD1_PKT_FMT, p_fmt) |
 +	      FIELD_PREP(MT_TXD1_OWN_MAC, omac_idx);
 +	txwi[1] = cpu_to_le32(val);
 +
 +	val = FIELD_PREP(MT_TXD2_FRAME_TYPE, fc_type) |
 +	      FIELD_PREP(MT_TXD2_SUB_TYPE, fc_stype) |
 +	      FIELD_PREP(MT_TXD2_MULTICAST, multicast);
 +	if (key) {
 +		if (multicast && ieee80211_is_robust_mgmt_frame(skb) &&
 +		    key->cipher == WLAN_CIPHER_SUITE_AES_CMAC) {
 +			val |= MT_TXD2_BIP;
 +			txwi[3] = 0;
 +		} else {
 +			txwi[3] = cpu_to_le32(MT_TXD3_PROTECT_FRAME);
 +		}
 +	} else {
 +		txwi[3] = 0;
 +	}
 +	txwi[2] = cpu_to_le32(val);
 +
 +	if (!(info->flags & IEEE80211_TX_CTL_AMPDU))
 +		txwi[2] |= cpu_to_le32(MT_TXD2_BA_DISABLE);
 +
 +	txwi[4] = 0;
 +	txwi[6] = 0;
 +
 +	if (rate->idx >= 0 && rate->count &&
 +	    !(info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)) {
 +		bool stbc = info->flags & IEEE80211_TX_CTL_STBC;
 +		u8 bw;
 +		u16 rateval = mt7615_mac_tx_rate_val(dev, mphy, rate, stbc,
 +						     &bw);
 +
 +		txwi[2] |= cpu_to_le32(MT_TXD2_FIX_RATE);
 +
 +		val = MT_TXD6_FIXED_BW |
 +		      FIELD_PREP(MT_TXD6_BW, bw) |
 +		      FIELD_PREP(MT_TXD6_TX_RATE, rateval);
 +		txwi[6] |= cpu_to_le32(val);
 +
 +		if (rate->flags & IEEE80211_TX_RC_SHORT_GI)
 +			txwi[6] |= cpu_to_le32(MT_TXD6_SGI);
 +
 +		if (info->flags & IEEE80211_TX_CTL_LDPC)
 +			txwi[6] |= cpu_to_le32(MT_TXD6_LDPC);
 +
 +		if (!(rate->flags & (IEEE80211_TX_RC_MCS |
 +				     IEEE80211_TX_RC_VHT_MCS)))
 +			txwi[2] |= cpu_to_le32(MT_TXD2_BA_DISABLE);
 +
 +		tx_count = rate->count;
 +	}
 +
 +	if (!ieee80211_is_beacon(fc)) {
 +		struct ieee80211_hw *hw = mt76_hw(dev);
 +
 +		val = MT_TXD5_TX_STATUS_HOST | FIELD_PREP(MT_TXD5_PID, pid);
 +		if (!ieee80211_hw_check(hw, SUPPORTS_PS))
 +			val |= MT_TXD5_SW_POWER_MGMT;
 +		txwi[5] = cpu_to_le32(val);
 +	} else {
 +		txwi[5] = 0;
 +		/* use maximum tx count for beacons */
 +		tx_count = 0x1f;
 +	}
 +
 +	val = FIELD_PREP(MT_TXD3_REM_TX_COUNT, tx_count);
 +	if (info->flags & IEEE80211_TX_CTL_INJECTED) {
 +		seqno = le16_to_cpu(hdr->seq_ctrl);
 +
 +		if (ieee80211_is_back_req(hdr->frame_control)) {
 +			struct ieee80211_bar *bar;
 +
 +			bar = (struct ieee80211_bar *)skb->data;
 +			seqno = le16_to_cpu(bar->start_seq_num);
 +		}
 +
 +		val |= MT_TXD3_SN_VALID |
 +		       FIELD_PREP(MT_TXD3_SEQ, IEEE80211_SEQ_TO_SN(seqno));
 +	}
 +
 +	txwi[3] |= cpu_to_le32(val);
 +
 +	if (info->flags & IEEE80211_TX_CTL_NO_ACK)
 +		txwi[3] |= cpu_to_le32(MT_TXD3_NO_ACK);
 +
 +	val = FIELD_PREP(MT_TXD7_TYPE, fc_type) |
 +	      FIELD_PREP(MT_TXD7_SUB_TYPE, fc_stype) |
 +	      FIELD_PREP(MT_TXD7_SPE_IDX, 0x18);
 +	txwi[7] = cpu_to_le32(val);
 +	if (!is_mmio) {
 +		val = FIELD_PREP(MT_TXD8_L_TYPE, fc_type) |
 +		      FIELD_PREP(MT_TXD8_L_SUB_TYPE, fc_stype);
 +		txwi[8] = cpu_to_le32(val);
 +	}
 +
 +	return 0;
 +}
 +EXPORT_SYMBOL_GPL(mt7615_mac_write_txwi);
 +
 +bool mt7615_mac_wtbl_update(struct mt7615_dev *dev, int idx, u32 mask)
 +{
 +	mt76_rmw(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_WLAN_IDX,
 +		 FIELD_PREP(MT_WTBL_UPDATE_WLAN_IDX, idx) | mask);
 +
 +	return mt76_poll(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY,
 +			 0, 5000);
 +}
 +
 +void mt7615_mac_sta_poll(struct mt7615_dev *dev)
 +{
 +	static const u8 ac_to_tid[4] = {
 +		[IEEE80211_AC_BE] = 0,
 +		[IEEE80211_AC_BK] = 1,
 +		[IEEE80211_AC_VI] = 4,
 +		[IEEE80211_AC_VO] = 6
 +	};
 +	static const u8 hw_queue_map[] = {
 +		[IEEE80211_AC_BK] = 0,
 +		[IEEE80211_AC_BE] = 1,
 +		[IEEE80211_AC_VI] = 2,
 +		[IEEE80211_AC_VO] = 3,
 +	};
 +	struct ieee80211_sta *sta;
 +	struct mt7615_sta *msta;
 +	u32 addr, tx_time[4], rx_time[4];
 +	struct list_head sta_poll_list;
 +	int i;
 +
 +	INIT_LIST_HEAD(&sta_poll_list);
 +	spin_lock_bh(&dev->mt76.sta_poll_lock);
 +	list_splice_init(&dev->mt76.sta_poll_list, &sta_poll_list);
 +	spin_unlock_bh(&dev->mt76.sta_poll_lock);
 +
 +	while (!list_empty(&sta_poll_list)) {
 +		bool clear = false;
 +
 +		msta = list_first_entry(&sta_poll_list, struct mt7615_sta,
 +					wcid.poll_list);
 +
 +		spin_lock_bh(&dev->mt76.sta_poll_lock);
 +		list_del_init(&msta->wcid.poll_list);
 +		spin_unlock_bh(&dev->mt76.sta_poll_lock);
 +
 +		addr = mt7615_mac_wtbl_addr(dev, msta->wcid.idx) + 19 * 4;
 +
 +		for (i = 0; i < 4; i++, addr += 8) {
 +			u32 tx_last = msta->airtime_ac[i];
 +			u32 rx_last = msta->airtime_ac[i + 4];
 +
 +			msta->airtime_ac[i] = mt76_rr(dev, addr);
 +			msta->airtime_ac[i + 4] = mt76_rr(dev, addr + 4);
 +			tx_time[i] = msta->airtime_ac[i] - tx_last;
 +			rx_time[i] = msta->airtime_ac[i + 4] - rx_last;
 +
 +			if ((tx_last | rx_last) & BIT(30))
 +				clear = true;
 +		}
 +
 +		if (clear) {
 +			mt7615_mac_wtbl_update(dev, msta->wcid.idx,
 +					       MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
 +			memset(msta->airtime_ac, 0, sizeof(msta->airtime_ac));
 +		}
 +
 +		if (!msta->wcid.sta)
 +			continue;
 +
 +		sta = container_of((void *)msta, struct ieee80211_sta,
 +				   drv_priv);
 +		for (i = 0; i < 4; i++) {
 +			u32 tx_cur = tx_time[i];
 +			u32 rx_cur = rx_time[hw_queue_map[i]];
 +			u8 tid = ac_to_tid[i];
 +
 +			if (!tx_cur && !rx_cur)
*** 11205 LINES SKIPPED ***


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?69e3f1a7.263cf.6ebf7817>