linux-wireless.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: "lorenzo.bianconi@redhat.com" <lorenzo.bianconi@redhat.com>
To: Ryder Lee <Ryder.Lee@mediatek.com>
Cc: "nbd@nbd.name" <nbd@nbd.name>,
	"lorenzo@kernel.org" <lorenzo@kernel.org>,
	"linux-wireless@vger.kernel.org" <linux-wireless@vger.kernel.org>
Subject: Re: [PATCH v2 15/15] wifi: mt76: connac: add connac3 mac library
Date: Fri, 9 Jun 2023 18:34:38 +0200	[thread overview]
Message-ID: <ZINUnqkElqSOITxT@localhost.localdomain> (raw)
In-Reply-To: <5010e5e508e89041451288659390fde5ded94db5.camel@mediatek.com>

[-- Attachment #1: Type: text/plain, Size: 29436 bytes --]

On Jun 09, Ryder Lee wrote:
> On Fri, 2023-06-09 at 10:15 +0200, Lorenzo Bianconi wrote:
> >  	 
> > External email : Please do not click links or open attachments until
> > you have verified the sender or the content.
> >  Introduce connac3_mac in mt76_connac library to reuse mac code
> > shared
> > between WiFi7 chipsets.
> > 
> > Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
> > ---
> >  drivers/net/wireless/mediatek/mt76/Makefile   |   2 +-
> >  .../net/wireless/mediatek/mt76/mt76_connac.h  |  19 +
> >  .../wireless/mediatek/mt76/mt76_connac3_mac.c | 742
> > +++++++++++++++++
> >  .../wireless/mediatek/mt76/mt76_connac3_mac.h |  18 +
> >  .../net/wireless/mediatek/mt76/mt7996/init.c  |   4 +-
> >  .../net/wireless/mediatek/mt76/mt7996/mac.c   | 761 +---------------
> > --
> >  .../net/wireless/mediatek/mt76/mt7996/main.c  |   8 +-
> >  .../net/wireless/mediatek/mt76/mt7996/mcu.c   |   9 +-
> >  .../wireless/mediatek/mt76/mt7996/mt7996.h    |  28 +-
> >  9 files changed, 807 insertions(+), 784 deletions(-)
> >  create mode 100644
> > drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.c
> > 
> > diff --git a/drivers/net/wireless/mediatek/mt76/Makefile
> > b/drivers/net/wireless/mediatek/mt76/Makefile
> > index 84c99b7e57f9..d8e8079c8b54 100644
> > --- a/drivers/net/wireless/mediatek/mt76/Makefile
> > +++ b/drivers/net/wireless/mediatek/mt76/Makefile
> > @@ -27,7 +27,7 @@ mt76x02-lib-y := mt76x02_util.o mt76x02_mac.o
> > mt76x02_mcu.o \
> >  
> >  mt76x02-usb-y := mt76x02_usb_mcu.o mt76x02_usb_core.o
> >  
> > -mt76-connac-lib-y := mt76_connac_mcu.o mt76_connac_mac.o
> > +mt76-connac-lib-y := mt76_connac_mcu.o mt76_connac_mac.o
> > mt76_connac3_mac.o
> >  
> >  obj-$(CONFIG_MT76x0_COMMON) += mt76x0/
> >  obj-$(CONFIG_MT76x2_COMMON) += mt76x2/
> > diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac.h
> > b/drivers/net/wireless/mediatek/mt76/mt76_connac.h
> > index 68bdeada1421..20111678537b 100644
> > --- a/drivers/net/wireless/mediatek/mt76/mt76_connac.h
> > +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac.h
> > @@ -415,4 +415,23 @@ void mt76_connac2_txwi_free(struct mt76_dev
> > *dev, struct mt76_txwi_cache *t,
> >  			    struct list_head *free_list);
> >  void mt76_connac2_tx_token_put(struct mt76_dev *dev);
> >  
> > +/* connac3 */
> > +void mt76_connac3_tx_check_aggr(struct ieee80211_sta *sta, __le32
> > *txwi);
> > +void mt76_connac3_mac_decode_he_radiotap(struct sk_buff *skb, __le32
> > *rxv,
> > +					 u8 mode);
> > +int mt76_connac3_mac_fill_rx_rate(struct mt76_dev *dev,
> > +				  struct mt76_rx_status *status,
> > +				  struct ieee80211_supported_band
> > *sband,
> > +				  __le32 *rxv, u8 *mode);
> > +void mt76_connac3_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi,
> > +				 struct sk_buff *skb, struct mt76_wcid
> > *wcid,
> > +				 struct ieee80211_key_conf *key, int
> > pid,
> > +				 enum mt76_txq_id qid, u32 changed);
> > +void mt76_connac3_txwi_free(struct mt76_dev *dev, struct
> > mt76_txwi_cache *t,
> > +			    struct ieee80211_sta *sta,
> > +			    struct list_head *free_list);
> > +void mt76_connac3_mac_add_txs(struct mt76_dev *dev, void *data,
> > +			      u32 max_wtbl_size);
> > +void mt76_connac3_tx_token_put(struct mt76_dev *dev);
> > +
> >  #endif /* __MT76_CONNAC_H */
> > diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.c
> > b/drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.c
> > new file mode 100644
> > index 000000000000..4b745bb74ca0
> > --- /dev/null
> > +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.c
> > @@ -0,0 +1,742 @@
> > +// SPDX-License-Identifier: ISC
> > +/* Copyright (C) 2023 MediaTek Inc. */
> > +
> > +#include "mt76_connac.h"
> > +#include "mt76_connac3_mac.h"
> > +#include "dma.h"
> > +
> > +#define HE_BITS(f)		cpu_to_le16(IEEE80211_RADIOTAP_HE_##f)
> > +#define HE_PREP(f, m, v)	le16_encode_bits(le32_get_bits(v,
> > MT_CRXV_HE_##m),\
> > +						 IEEE80211_RADIOTAP_HE_
> > ##f)
> > +
> > +void mt76_connac3_tx_check_aggr(struct ieee80211_sta *sta, __le32
> > *txwi)
> > +{
> > +	struct mt76_wcid *wcid;
> > +	u16 fc, tid;
> > +	u32 val;
> > +
> > +	if (!sta ||
> > +	    !(sta->deflink.ht_cap.ht_supported || sta-
> > >deflink.he_cap.has_he))
> > +		return;
> > +
> > +	tid = le32_get_bits(txwi[1], MT_TXD1_TID);
> > +	if (tid >= 6) /* skip VO queue */
> > +		return;
> > +
> > +	val = le32_to_cpu(txwi[2]);
> > +	fc = FIELD_GET(MT_TXD2_FRAME_TYPE, val) << 2 |
> > +	     FIELD_GET(MT_TXD2_SUB_TYPE, val) << 4;
> > +	if (unlikely(fc != (IEEE80211_FTYPE_DATA |
> > IEEE80211_STYPE_QOS_DATA)))
> > +		return;
> > +
> > +	wcid = (struct mt76_wcid *)sta->drv_priv;
> > +	if (!test_and_set_bit(tid, &wcid->ampdu_state))
> > +		ieee80211_start_tx_ba_session(sta, tid, 0);
> > +}
> > +EXPORT_SYMBOL_GPL(mt76_connac3_tx_check_aggr);
> > +
> > +static void
> > +mt76_connac3_mac_decode_he_radiotap_ru(struct mt76_rx_status
> > *status,
> > +				       struct ieee80211_radiotap_he
> > *he,
> > +				       __le32 *rxv)
> > +{
> > +	u32 ru = le32_get_bits(rxv[0], MT_PRXV_HE_RU_ALLOC), offs = 0;
> > +
> > +	status->bw = RATE_INFO_BW_HE_RU;
> > +
> > +	switch (ru) {
> > +	case 0 ... 36:
> > +		status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_26;
> > +		offs = ru;
> > +		break;
> > +	case 37 ... 52:
> > +		status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_52;
> > +		offs = ru - 37;
> > +		break;
> > +	case 53 ... 60:
> > +		status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_106;
> > +		offs = ru - 53;
> > +		break;
> > +	case 61 ... 64:
> > +		status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_242;
> > +		offs = ru - 61;
> > +		break;
> > +	case 65 ... 66:
> > +		status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_484;
> > +		offs = ru - 65;
> > +		break;
> > +	case 67:
> > +		status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_996;
> > +		break;
> > +	case 68:
> > +		status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_2x996;
> > +		break;
> > +	}
> > +
> > +	he->data1 |= HE_BITS(DATA1_BW_RU_ALLOC_KNOWN);
> > +	he->data2 |= HE_BITS(DATA2_RU_OFFSET_KNOWN) |
> > +		     le16_encode_bits(offs,
> > +				      IEEE80211_RADIOTAP_HE_DATA2_RU_OF
> > FSET);
> > +}
> > +
> > +#define MU_PREP(f, v)	le16_encode_bits(v,
> > IEEE80211_RADIOTAP_HE_MU_##f)
> > +static void
> > +mt76_connac3_mac_decode_he_mu_radiotap(struct sk_buff *skb, __le32
> > *rxv)
> > +{
> > +	struct mt76_rx_status *status = (struct mt76_rx_status *)skb-
> > >cb;
> > +	static const struct ieee80211_radiotap_he_mu mu_known = {
> > +		.flags1 = HE_BITS(MU_FLAGS1_SIG_B_MCS_KNOWN) |
> > +			  HE_BITS(MU_FLAGS1_SIG_B_DCM_KNOWN) |
> > +			  HE_BITS(MU_FLAGS1_CH1_RU_KNOWN) |
> > +			  HE_BITS(MU_FLAGS1_SIG_B_SYMS_USERS_KNOWN),
> > +		.flags2 = HE_BITS(MU_FLAGS2_BW_FROM_SIG_A_BW_KNOWN),
> > +	};
> > +	struct ieee80211_radiotap_he_mu *he_mu;
> > +
> > +	status->flag |= RX_FLAG_RADIOTAP_HE_MU;
> > +
> > +	he_mu = skb_push(skb, sizeof(mu_known));
> > +	memcpy(he_mu, &mu_known, sizeof(mu_known));
> > +
> > +	he_mu->flags1 |= MU_PREP(FLAGS1_SIG_B_MCS, status->rate_idx);
> > +	if (status->he_dcm)
> > +		he_mu->flags1 |= MU_PREP(FLAGS1_SIG_B_DCM, status-
> > >he_dcm);
> > +
> > +	he_mu->flags2 |= MU_PREP(FLAGS2_BW_FROM_SIG_A_BW, status->bw) |
> > +			 MU_PREP(FLAGS2_SIG_B_SYMS_USERS,
> > +				 le32_get_bits(rxv[4],
> > MT_CRXV_HE_NUM_USER));
> > +
> > +	he_mu->ru_ch1[0] = le32_get_bits(rxv[16], MT_CRXV_HE_RU0) &
> > 0xff;
> > +
> > +	if (status->bw >= RATE_INFO_BW_40) {
> > +		he_mu->flags1 |= HE_BITS(MU_FLAGS1_CH2_RU_KNOWN);
> > +		he_mu->ru_ch2[0] = le32_get_bits(rxv[16],
> > MT_CRXV_HE_RU1) & 0xff;
> > +	}
> > +
> > +	if (status->bw >= RATE_INFO_BW_80) {
> > +		u32 ru_h, ru_l;
> > +
> > +		he_mu->ru_ch1[1] = le32_get_bits(rxv[16],
> > MT_CRXV_HE_RU2) & 0xff;
> > +
> > +		ru_l = le32_get_bits(rxv[16], MT_CRXV_HE_RU3_L);
> > +		ru_h = le32_get_bits(rxv[17], MT_CRXV_HE_RU3_H) & 0x7;
> > +		he_mu->ru_ch2[1] = (u8)(ru_l | ru_h << 4);
> > +	}
> > +}
> > +
> > +void mt76_connac3_mac_decode_he_radiotap(struct sk_buff *skb, __le32
> > *rxv,
> > +					 u8 mode)
> > +{
> > +	struct mt76_rx_status *status = (struct mt76_rx_status *)skb-
> > >cb;
> > +	static const struct ieee80211_radiotap_he known = {
> > +		.data1 = HE_BITS(DATA1_DATA_MCS_KNOWN) |
> > +			 HE_BITS(DATA1_DATA_DCM_KNOWN) |
> > +			 HE_BITS(DATA1_STBC_KNOWN) |
> > +			 HE_BITS(DATA1_CODING_KNOWN) |
> > +			 HE_BITS(DATA1_LDPC_XSYMSEG_KNOWN) |
> > +			 HE_BITS(DATA1_DOPPLER_KNOWN) |
> > +			 HE_BITS(DATA1_SPTL_REUSE_KNOWN) |
> > +			 HE_BITS(DATA1_BSS_COLOR_KNOWN),
> > +		.data2 = HE_BITS(DATA2_GI_KNOWN) |
> > +			 HE_BITS(DATA2_TXBF_KNOWN) |
> > +			 HE_BITS(DATA2_PE_DISAMBIG_KNOWN) |
> > +			 HE_BITS(DATA2_TXOP_KNOWN),
> > +	};
> > +	u32 ltf_size = le32_get_bits(rxv[4], MT_CRXV_HE_LTF_SIZE) + 1;
> > +	struct ieee80211_radiotap_he *he;
> > +
> > +	status->flag |= RX_FLAG_RADIOTAP_HE;
> > +
> > +	he = skb_push(skb, sizeof(known));
> > +	memcpy(he, &known, sizeof(known));
> > +
> > +	he->data3 = HE_PREP(DATA3_BSS_COLOR, BSS_COLOR, rxv[9]) |
> > +		    HE_PREP(DATA3_LDPC_XSYMSEG, LDPC_EXT_SYM, rxv[4]);
> > +	he->data4 = HE_PREP(DATA4_SU_MU_SPTL_REUSE, SR_MASK, rxv[13]);
> > +	he->data5 = HE_PREP(DATA5_PE_DISAMBIG, PE_DISAMBIG, rxv[5]) |
> > +		    le16_encode_bits(ltf_size,
> > +				     IEEE80211_RADIOTAP_HE_DATA5_LTF_SI
> > ZE);
> > +	if (le32_to_cpu(rxv[0]) & MT_PRXV_TXBF)
> > +		he->data5 |= HE_BITS(DATA5_TXBF);
> > +	he->data6 = HE_PREP(DATA6_TXOP, TXOP_DUR, rxv[9]) |
> > +		    HE_PREP(DATA6_DOPPLER, DOPPLER, rxv[9]);
> > +
> > +	switch (mode) {
> > +	case MT_PHY_TYPE_HE_SU:
> > +		he->data1 |= HE_BITS(DATA1_FORMAT_SU) |
> > +			     HE_BITS(DATA1_UL_DL_KNOWN) |
> > +			     HE_BITS(DATA1_BEAM_CHANGE_KNOWN) |
> > +			     HE_BITS(DATA1_BW_RU_ALLOC_KNOWN);
> > +
> > +		he->data3 |= HE_PREP(DATA3_BEAM_CHANGE, BEAM_CHNG,
> > rxv[8]) |
> > +			     HE_PREP(DATA3_UL_DL, UPLINK, rxv[5]);
> > +		break;
> > +	case MT_PHY_TYPE_HE_EXT_SU:
> > +		he->data1 |= HE_BITS(DATA1_FORMAT_EXT_SU) |
> > +			     HE_BITS(DATA1_UL_DL_KNOWN) |
> > +			     HE_BITS(DATA1_BW_RU_ALLOC_KNOWN);
> > +
> > +		he->data3 |= HE_PREP(DATA3_UL_DL, UPLINK, rxv[5]);
> > +		break;
> > +	case MT_PHY_TYPE_HE_MU:
> > +		he->data1 |= HE_BITS(DATA1_FORMAT_MU) |
> > +			     HE_BITS(DATA1_UL_DL_KNOWN);
> > +
> > +		he->data3 |= HE_PREP(DATA3_UL_DL, UPLINK, rxv[5]);
> > +		he->data4 |= HE_PREP(DATA4_MU_STA_ID, MU_AID, rxv[8]);
> > +
> > +		mt76_connac3_mac_decode_he_radiotap_ru(status, he,
> > rxv);
> > +		mt76_connac3_mac_decode_he_mu_radiotap(skb, rxv);
> > +		break;
> > +	case MT_PHY_TYPE_HE_TB:
> > +		he->data1 |= HE_BITS(DATA1_FORMAT_TRIG) |
> > +			     HE_BITS(DATA1_SPTL_REUSE2_KNOWN) |
> > +			     HE_BITS(DATA1_SPTL_REUSE3_KNOWN) |
> > +			     HE_BITS(DATA1_SPTL_REUSE4_KNOWN);
> > +
> > +		he->data4 |= HE_PREP(DATA4_TB_SPTL_REUSE1, SR_MASK,
> > rxv[13]) |
> > +			     HE_PREP(DATA4_TB_SPTL_REUSE2, SR1_MASK,
> > rxv[13]) |
> > +			     HE_PREP(DATA4_TB_SPTL_REUSE3, SR2_MASK,
> > rxv[13]) |
> > +			     HE_PREP(DATA4_TB_SPTL_REUSE4, SR3_MASK,
> > rxv[13]);
> > +
> > +		mt76_connac3_mac_decode_he_radiotap_ru(status, he,
> > rxv);
> > +		break;
> > +	default:
> > +		break;
> > +	}
> > +}
> > +EXPORT_SYMBOL_GPL(mt76_connac3_mac_decode_he_radiotap);
> > +
> > +int mt76_connac3_mac_fill_rx_rate(struct mt76_dev *dev,
> > +				  struct mt76_rx_status *status,
> > +				  struct ieee80211_supported_band
> > *sband,
> > +				  __le32 *rxv, u8 *mode)
> > +{
> > +	u8 stbc, gi, bw, dcm, nss;
> > +	bool cck = false;
> > +	u32 v0, v2;
> > +	int i, idx;
> > +
> > +	v0 = le32_to_cpu(rxv[0]);
> > +	v2 = le32_to_cpu(rxv[2]);
> > +
> > +	idx = FIELD_GET(MT_PRXV_TX_RATE, v0);
> > +	i = idx;
> > +	nss = FIELD_GET(MT_PRXV_NSTS, v0) + 1;
> > +
> > +	stbc = FIELD_GET(MT_PRXV_HT_STBC, v2);
> > +	gi = FIELD_GET(MT_PRXV_HT_SHORT_GI, v2);
> > +	*mode = FIELD_GET(MT_PRXV_TX_MODE, v2);
> > +	dcm = FIELD_GET(MT_PRXV_DCM, v2);
> > +	bw = FIELD_GET(MT_PRXV_FRAME_MODE, v2);
> > +
> > +	switch (*mode) {
> > +	case MT_PHY_TYPE_CCK:
> > +		cck = true;
> > +		fallthrough;
> > +	case MT_PHY_TYPE_OFDM:
> > +		i = mt76_get_rate(dev, sband, i, cck);
> > +		break;
> > +	case MT_PHY_TYPE_HT_GF:
> > +	case MT_PHY_TYPE_HT:
> > +		status->encoding = RX_ENC_HT;
> > +		if (gi)
> > +			status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
> > +		if (i > 31)
> > +			return -EINVAL;
> > +		break;
> > +	case MT_PHY_TYPE_VHT:
> > +		status->nss = nss;
> > +		status->encoding = RX_ENC_VHT;
> > +		if (gi)
> > +			status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
> > +		if (i > 11)
> > +			return -EINVAL;
> > +		break;
> > +	case MT_PHY_TYPE_HE_MU:
> > +	case MT_PHY_TYPE_HE_SU:
> > +	case MT_PHY_TYPE_HE_EXT_SU:
> > +	case MT_PHY_TYPE_HE_TB:
> > +		status->nss = nss;
> > +		status->encoding = RX_ENC_HE;
> > +		i &= GENMASK(3, 0);
> > +
> > +		if (gi <= NL80211_RATE_INFO_HE_GI_3_2)
> > +			status->he_gi = gi;
> > +
> > +		status->he_dcm = dcm;
> > +		break;
> > +	case MT_PHY_TYPE_EHT_SU:
> > +	case MT_PHY_TYPE_EHT_TRIG:
> > +	case MT_PHY_TYPE_EHT_MU:
> > +		status->nss = nss;
> > +		status->encoding = RX_ENC_EHT;
> > +		i &= GENMASK(3, 0);
> > +
> > +		if (gi <= NL80211_RATE_INFO_EHT_GI_3_2)
> > +			status->eht.gi = gi;
> > +		break;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +	status->rate_idx = i;
> > +
> > +	switch (bw) {
> > +	case IEEE80211_STA_RX_BW_20:
> > +		break;
> > +	case IEEE80211_STA_RX_BW_40:
> > +		if (*mode & MT_PHY_TYPE_HE_EXT_SU &&
> > +		    (idx & MT_PRXV_TX_ER_SU_106T)) {
> > +			status->bw = RATE_INFO_BW_HE_RU;
> > +			status->he_ru =
> > +				NL80211_RATE_INFO_HE_RU_ALLOC_106;
> > +		} else {
> > +			status->bw = RATE_INFO_BW_40;
> > +		}
> > +		break;
> > +	case IEEE80211_STA_RX_BW_80:
> > +		status->bw = RATE_INFO_BW_80;
> > +		break;
> > +	case IEEE80211_STA_RX_BW_160:
> > +		status->bw = RATE_INFO_BW_160;
> > +		break;
> > +	case IEEE80211_STA_RX_BW_320:
> > +		status->bw = RATE_INFO_BW_320;
> > +		break;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +
> > +	status->enc_flags |= RX_ENC_FLAG_STBC_MASK * stbc;
> > +	if (*mode < MT_PHY_TYPE_HE_SU && gi)
> > +		status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
> > +
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(mt76_connac3_mac_fill_rx_rate);
> > +
> > +static void
> > +mt76_connac3_mac_write_txwi_8023(__le32 *txwi, struct sk_buff *skb,
> > +				 struct mt76_wcid *wcid)
> > +{
> > +	u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
> > +	u8 fc_type, fc_stype;
> > +	u16 ethertype;
> > +	bool wmm = false;
> > +	u32 val;
> > +
> > +	if (wcid->sta) {
> > +		struct ieee80211_sta *sta;
> > +
> > +		sta = container_of((void *)wcid, struct ieee80211_sta,
> > drv_priv);
> > +		wmm = sta->wme;
> > +	}
> > +
> > +	val = FIELD_PREP(MT_TXD1_HDR_FORMAT, MT_HDR_FORMAT_802_3) |
> > +	      FIELD_PREP(MT_TXD1_TID, tid);
> > +
> > +	ethertype = get_unaligned_be16(&skb->data[12]);
> > +	if (ethertype >= ETH_P_802_3_MIN)
> > +		val |= MT_TXD1_ETH_802_3;
> > +
> > +	txwi[1] |= cpu_to_le32(val);
> > +
> > +	fc_type = IEEE80211_FTYPE_DATA >> 2;
> > +	fc_stype = wmm ? IEEE80211_STYPE_QOS_DATA >> 4 : 0;
> > +
> > +	val = FIELD_PREP(MT_TXD2_FRAME_TYPE, fc_type) |
> > +	      FIELD_PREP(MT_TXD2_SUB_TYPE, fc_stype);
> > +
> > +	txwi[2] |= cpu_to_le32(val);
> > +}
> > +
> > +static void
> > +mt76_connac3_mac_write_txwi_80211(struct mt76_dev *dev, __le32
> > *txwi,
> > +				  struct sk_buff *skb,
> > +				  struct ieee80211_key_conf *key)
> > +{
> > +	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
> > +	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb-
> > >data;
> > +	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
> > +	bool multicast = is_multicast_ether_addr(hdr->addr1);
> > +	u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
> > +	__le16 fc = hdr->frame_control;
> > +	u8 fc_type, fc_stype;
> > +	u32 val;
> > +
> > +	if (ieee80211_is_action(fc) &&
> > +	    mgmt->u.action.category == WLAN_CATEGORY_BACK &&
> > +	    mgmt->u.action.u.addba_req.action_code ==
> > WLAN_ACTION_ADDBA_REQ)
> > +		tid = MT_TX_ADDBA;
> > +	else if (ieee80211_is_mgmt(hdr->frame_control))
> > +		tid = MT_TX_NORMAL;
> > +
> > +	val = 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, tid);
> > +
> > +	if (!ieee80211_is_data(fc) || multicast ||
> > +	    info->flags & IEEE80211_TX_CTL_USE_MINRATE)
> > +		val |= MT_TXD1_FIXED_RATE;
> > +
> > +	if (key && multicast && ieee80211_is_robust_mgmt_frame(skb) &&
> > +	    key->cipher == WLAN_CIPHER_SUITE_AES_CMAC) {
> > +		val |= MT_TXD1_BIP;
> > +		txwi[3] &= ~cpu_to_le32(MT_TXD3_PROTECT_FRAME);
> > +	}
> > +
> > +	txwi[1] |= cpu_to_le32(val);
> > +
> > +	fc_type = (le16_to_cpu(fc) & IEEE80211_FCTL_FTYPE) >> 2;
> > +	fc_stype = (le16_to_cpu(fc) & IEEE80211_FCTL_STYPE) >> 4;
> > +
> > +	val = FIELD_PREP(MT_TXD2_FRAME_TYPE, fc_type) |
> > +	      FIELD_PREP(MT_TXD2_SUB_TYPE, fc_stype);
> > +
> > +	txwi[2] |= cpu_to_le32(val);
> > +
> > +	txwi[3] |= cpu_to_le32(FIELD_PREP(MT_TXD3_BCM, multicast));
> > +	if (ieee80211_is_beacon(fc)) {
> > +		txwi[3] &= ~cpu_to_le32(MT_TXD3_SW_POWER_MGMT);
> > +		txwi[3] |= cpu_to_le32(MT_TXD3_REM_TX_COUNT);
> > +	}
> > +
> > +	if (info->flags & IEEE80211_TX_CTL_INJECTED) {
> > +		u16 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);
> > +		txwi[3] &= ~cpu_to_le32(MT_TXD3_HW_AMSDU);
> > +	}
> > +}
> > +
> > +void mt76_connac3_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi,
> > +				 struct sk_buff *skb, struct mt76_wcid
> > *wcid,
> > +				 struct ieee80211_key_conf *key, int
> > pid,
> > +				 enum mt76_txq_id qid, u32 changed)
> > +{
> > +	u32 val, sz_txd = mt76_is_mmio(dev) ? MT_TXD_SIZE :
> > MT_SDIO_TXD_SIZE;
> > +	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
> > +	struct ieee80211_vif *vif = info->control.vif;
> > +	u8 band_idx = (info->hw_queue & MT_TX_HW_QUEUE_PHY) >> 2;
> > +	u8 p_fmt, q_idx, omac_idx = 0, wmm_idx = 0;
> > +	bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP;
> > +	struct mt76_vif *mvif;
> > +	u16 tx_count = 15;
> > +	bool beacon = !!(changed & (BSS_CHANGED_BEACON |
> > +				    BSS_CHANGED_BEACON_ENABLED));
> > +	bool inband_disc = !!(changed &
> > (BSS_CHANGED_UNSOL_BCAST_PROBE_RESP |
> > +					 BSS_CHANGED_FILS_DISCOVERY));
> > +
> > +	mvif = vif ? (struct mt76_vif *)vif->drv_priv : NULL;
> > +	if (mvif) {
> > +		omac_idx = mvif->omac_idx;
> > +		wmm_idx = mvif->wmm_idx;
> > +		band_idx = mvif->band_idx;
> > +	}
> > +
> > +	if (inband_disc) {
> > +		p_fmt = MT_TX_TYPE_FW;
> > +		q_idx = MT_LMAC_ALTX0;
> > +	} else if (beacon) {
> > +		p_fmt = MT_TX_TYPE_FW;
> > +		q_idx = MT_LMAC_BCN0;
> > +	} else if (qid >= MT_TXQ_PSD) {
> > +		p_fmt = mt76_is_mmio(dev) ? MT_TX_TYPE_CT :
> > MT_TX_TYPE_SF;
> > +		q_idx = MT_LMAC_ALTX0;
> > +	} else {
> > +		p_fmt = mt76_is_mmio(dev) ? MT_TX_TYPE_CT :
> > MT_TX_TYPE_SF;
> > +		q_idx = wmm_idx * MT76_CONNAC_MAX_WMM_SETS +
> > +			mt76_connac_lmac_mapping(skb_get_queue_mapping(
> > skb));
> > +	}
> > +
> > +	val = FIELD_PREP(MT_TXD0_TX_BYTES, skb->len + sz_txd) |
> > +	      FIELD_PREP(MT_TXD0_PKT_FMT, p_fmt) |
> > +	      FIELD_PREP(MT_TXD0_Q_IDX, q_idx);
> > +	txwi[0] = cpu_to_le32(val);
> > +
> > +	val = FIELD_PREP(MT_TXD1_WLAN_IDX, wcid->idx) |
> > +	      FIELD_PREP(MT_TXD1_OWN_MAC, omac_idx);
> > +
> > +	if (band_idx)
> > +		val |= FIELD_PREP(MT_TXD1_TGID, band_idx);
> > +
> > +	txwi[1] = cpu_to_le32(val);
> > +	txwi[2] = 0;
> > +
> > +	val = MT_TXD3_SW_POWER_MGMT |
> > +	      FIELD_PREP(MT_TXD3_REM_TX_COUNT, tx_count);
> > +	if (key)
> > +		val |= MT_TXD3_PROTECT_FRAME;
> > +	if (info->flags & IEEE80211_TX_CTL_NO_ACK)
> > +		val |= MT_TXD3_NO_ACK;
> > +	if (wcid->amsdu)
> > +		val |= MT_TXD3_HW_AMSDU;
> > +
> > +	txwi[3] = cpu_to_le32(val);
> > +	txwi[4] = 0;
> > +
> > +	val = FIELD_PREP(MT_TXD5_PID, pid);
> > +	if (pid >= MT_PACKET_ID_FIRST)
> > +		val |= MT_TXD5_TX_STATUS_HOST;
> > +	txwi[5] = cpu_to_le32(val);
> > +
> > +	val = MT_TXD6_DIS_MAT | MT_TXD6_DAS |
> > +	      FIELD_PREP(MT_TXD6_MSDU_CNT, 1);
> > +	txwi[6] = cpu_to_le32(val);
> > +	txwi[7] = 0;
> > +
> > +	if (is_8023)
> > +		mt76_connac3_mac_write_txwi_8023(txwi, skb, wcid);
> > +	else
> > +		mt76_connac3_mac_write_txwi_80211(dev, txwi, skb, key);
> > +
> > +	if (txwi[1] & cpu_to_le32(MT_TXD1_FIXED_RATE)) {
> > +		struct ieee80211_hdr *hdr = (struct ieee80211_hdr
> > *)skb->data;
> > +		bool mcast = ieee80211_is_data(hdr->frame_control) &&
> > +			     is_multicast_ether_addr(hdr->addr1);
> > +		u8 idx = MT76_CONNAC3_BASIC_RATES_TBL;
> > +
> > +		if (mvif) {
> > +			if (mcast && mvif->mcast_rates_idx)
> > +				idx = mvif->mcast_rates_idx;
> > +			else if (beacon && mvif->beacon_rates_idx)
> > +				idx = mvif->beacon_rates_idx;
> > +			else
> > +				idx = mvif->basic_rates_idx;
> > +		}
> > +
> > +		txwi[6] |= cpu_to_le32(FIELD_PREP(MT_TXD6_TX_RATE,
> > idx));
> > +		txwi[3] |= cpu_to_le32(MT_TXD3_BA_DISABLE);
> > +	}
> > +}
> > +EXPORT_SYMBOL_GPL(mt76_connac3_mac_write_txwi);
> > +
> > +void mt76_connac3_txwi_free(struct mt76_dev *dev, struct
> > mt76_txwi_cache *t,
> > +			    struct ieee80211_sta *sta,
> > +			    struct list_head *free_list)
> > +{
> > +	__le32 *txwi;
> > +	u16 wcid_idx;
> > +
> > +	mt76_connac_txp_skb_unmap(dev, t);
> > +	if (!t->skb)
> > +		goto out;
> > +
> > +	txwi = (__le32 *)mt76_get_txwi_ptr(dev, t);
> > +	if (sta) {
> > +		struct mt76_wcid *wcid = (struct mt76_wcid *)sta-
> > >drv_priv;
> > +
> > +		wcid_idx = wcid->idx;
> > +		if (likely(t->skb->protocol != cpu_to_be16(ETH_P_PAE)))
> > +			mt76_connac3_tx_check_aggr(sta, txwi);
> > +	} else {
> > +		wcid_idx = le32_get_bits(txwi[1], MT_TXD1_WLAN_IDX);
> > +	}
> > +
> > +	__mt76_tx_complete_skb(dev, wcid_idx, t->skb, free_list);
> > +out:
> > +	t->skb = NULL;
> > +	mt76_put_txwi(dev, t);
> > +}
> > +EXPORT_SYMBOL_GPL(mt76_connac3_txwi_free);
> > +
> > +static bool
> > +mt76_connac3_mac_add_txs_skb(struct mt76_dev *dev, struct mt76_wcid
> > *wcid,
> > +			     int pid, __le32 *txs_data)
> > +{
> > +	struct mt76_sta_stats *stats = &wcid->stats;
> > +	struct ieee80211_supported_band *sband;
> > +	struct ieee80211_tx_info *info;
> > +	u32 txrate, txs, mode, stbc;
> > +	struct rate_info rate = {};
> > +	struct sk_buff_head list;
> > +	struct mt76_phy *mphy;
> > +	struct sk_buff *skb;
> > +	bool cck = false;
> > +
> > +	mt76_tx_status_lock(dev, &list);
> > +	skb = mt76_tx_status_skb_get(dev, wcid, pid, &list);
> > +	if (!skb)
> > +		goto out_no_skb;
> > +
> > +	txs = le32_to_cpu(txs_data[0]);
> > +
> > +	info = IEEE80211_SKB_CB(skb);
> > +	if (!(txs & MT_TXS0_ACK_ERROR_MASK))
> > +		info->flags |= IEEE80211_TX_STAT_ACK;
> > +
> > +	info->status.ampdu_len = 1;
> > +	info->status.ampdu_ack_len = !!(info->flags &
> > +					IEEE80211_TX_STAT_ACK);
> > +	info->status.rates[0].idx = -1;
> > +
> > +	txrate = FIELD_GET(MT_TXS0_TX_RATE, txs);
> > +
> > +	rate.mcs = FIELD_GET(MT_TX_RATE_IDX, txrate);
> > +	rate.nss = FIELD_GET(MT_TX_RATE_NSS, txrate) + 1;
> > +	stbc = le32_get_bits(txs_data[3], MT_TXS3_RATE_STBC);
> > +
> > +	if (stbc && rate.nss > 1)
> > +		rate.nss >>= 1;
> > +
> > +	if (rate.nss - 1 < ARRAY_SIZE(stats->tx_nss))
> > +		stats->tx_nss[rate.nss - 1]++;
> > +	if (rate.mcs < ARRAY_SIZE(stats->tx_mcs))
> > +		stats->tx_mcs[rate.mcs]++;
> > +
> > +	mode = FIELD_GET(MT_TX_RATE_MODE, txrate);
> > +	switch (mode) {
> > +	case MT_PHY_TYPE_CCK:
> > +		cck = true;
> > +		fallthrough;
> > +	case MT_PHY_TYPE_OFDM:
> > +		mphy = mt76_dev_phy(dev, wcid->phy_idx);
> > +
> > +		if (mphy->chandef.chan->band == NL80211_BAND_5GHZ)
> > +			sband = &mphy->sband_5g.sband;
> > +		else if (mphy->chandef.chan->band == NL80211_BAND_6GHZ)
> > +			sband = &mphy->sband_6g.sband;
> > +		else
> > +			sband = &mphy->sband_2g.sband;
> > +
> > +		rate.mcs = mt76_get_rate(mphy->dev, sband, rate.mcs,
> > cck);
> > +		rate.legacy = sband->bitrates[rate.mcs].bitrate;
> > +		break;
> > +	case MT_PHY_TYPE_HT:
> > +	case MT_PHY_TYPE_HT_GF:
> > +		if (rate.mcs > 31)
> > +			goto out;
> > +
> > +		rate.flags = RATE_INFO_FLAGS_MCS;
> > +		if (wcid->rate.flags & RATE_INFO_FLAGS_SHORT_GI)
> > +			rate.flags |= RATE_INFO_FLAGS_SHORT_GI;
> > +		break;
> > +	case MT_PHY_TYPE_VHT:
> > +		if (rate.mcs > 9)
> > +			goto out;
> > +
> > +		rate.flags = RATE_INFO_FLAGS_VHT_MCS;
> > +		break;
> > +	case MT_PHY_TYPE_HE_SU:
> > +	case MT_PHY_TYPE_HE_EXT_SU:
> > +	case MT_PHY_TYPE_HE_TB:
> > +	case MT_PHY_TYPE_HE_MU:
> > +		if (rate.mcs > 11)
> > +			goto out;
> > +
> > +		rate.he_gi = wcid->rate.he_gi;
> > +		rate.he_dcm = FIELD_GET(MT_TX_RATE_DCM, txrate);
> > +		rate.flags = RATE_INFO_FLAGS_HE_MCS;
> > +		break;
> > +	case MT_PHY_TYPE_EHT_SU:
> > +	case MT_PHY_TYPE_EHT_TRIG:
> > +	case MT_PHY_TYPE_EHT_MU:
> > +		if (rate.mcs > 13)
> > +			goto out;
> > +
> > +		rate.eht_gi = wcid->rate.eht_gi;
> > +		rate.flags = RATE_INFO_FLAGS_EHT_MCS;
> > +		break;
> > +	default:
> > +		goto out;
> > +	}
> > +
> > +	stats->tx_mode[mode]++;
> > +
> > +	switch (FIELD_GET(MT_TXS0_BW, txs)) {
> > +	case IEEE80211_STA_RX_BW_320:
> > +		rate.bw = RATE_INFO_BW_320;
> > +		stats->tx_bw[4]++;
> > +		break;
> > +	case IEEE80211_STA_RX_BW_160:
> > +		rate.bw = RATE_INFO_BW_160;
> > +		stats->tx_bw[3]++;
> > +		break;
> > +	case IEEE80211_STA_RX_BW_80:
> > +		rate.bw = RATE_INFO_BW_80;
> > +		stats->tx_bw[2]++;
> > +		break;
> > +	case IEEE80211_STA_RX_BW_40:
> > +		rate.bw = RATE_INFO_BW_40;
> > +		stats->tx_bw[1]++;
> > +		break;
> > +	default:
> > +		rate.bw = RATE_INFO_BW_20;
> > +		stats->tx_bw[0]++;
> > +		break;
> > +	}
> > +	wcid->rate = rate;
> > +
> > +out:
> > +	mt76_tx_status_skb_done(dev, skb, &list);
> > +
> > +out_no_skb:
> > +	mt76_tx_status_unlock(dev, &list);
> > +
> > +	return !!skb;
> > +}
> > +
> > +void mt76_connac3_mac_add_txs(struct mt76_dev *dev, void *data,
> > +			      u32 max_wtbl_size)
> > +{
> > +	struct mt76_wcid *wcid;
> > +	__le32 *txs_data = data;
> > +	u16 wcidx;
> > +	u8 pid;
> > +
> > +	if (le32_get_bits(txs_data[0], MT_TXS0_TXS_FORMAT) > 1)
> > +		return;
> > +
> > +	wcidx = le32_get_bits(txs_data[2], MT_TXS2_WCID);
> > +	pid = le32_get_bits(txs_data[3], MT_TXS3_PID);
> > +
> > +	if (pid < MT_PACKET_ID_FIRST)
> > +		return;
> > +
> > +	if (wcidx >= max_wtbl_size)
> > +		return;
> > +
> > +	rcu_read_lock();
> > +
> > +	wcid = rcu_dereference(dev->wcid[wcidx]);
> > +	if (!wcid)
> > +		goto out;
> > +
> > +	mt76_connac3_mac_add_txs_skb(dev, wcid, pid, txs_data);
> > +	if (!wcid->sta)
> > +		goto out;
> > +
> > +	spin_lock_bh(&dev->sta_poll_lock);
> > +	if (list_empty(&wcid->poll_list))
> > +		list_add_tail(&wcid->poll_list, &dev->sta_poll_list);
> > +	spin_unlock_bh(&dev->sta_poll_lock);
> > +
> > +out:
> > +	rcu_read_unlock();
> > +}
> > +EXPORT_SYMBOL_GPL(mt76_connac3_mac_add_txs);
> > +
> > +void mt76_connac3_tx_token_put(struct mt76_dev *dev)
> > +{
> > +	struct mt76_txwi_cache *txwi;
> > +	int id;
> > +
> > +	spin_lock_bh(&dev->token_lock);
> > +	idr_for_each_entry(&dev->token, txwi, id) {
> > +		mt76_connac3_txwi_free(dev, txwi, NULL, NULL);
> > +		dev->token_count--;
> > +	}
> > +	spin_unlock_bh(&dev->token_lock);
> > +	idr_destroy(&dev->token);
> > +}
> > +EXPORT_SYMBOL_GPL(mt76_connac3_tx_token_put);
> > diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.h
> > b/drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.h
> > index 6663a0b46541..bcc1d976b2b0 100644
> > --- a/drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.h
> > +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.h
> > @@ -4,6 +4,24 @@
> >  #ifndef __MT76_CONNAC3_MAC_H
> >  #define __MT76_CONNAC3_MAC_H
> >  
> > +/* NOTE: used to map mt76_rates. idx may change if firmware expands
> > table */
> > +#define MT76_CONNAC3_BASIC_RATES_TBL	11
> > +#define MT76_CONNAC3_BEACON_RATES_TBL	25
> > 
> 
> Different devices may have different defined value. 

The other WiFi7 device I am working on relies on the values I moved in
mt76_connac3_mac.h (in common with mt7996). Moreover you can still
have per-device values in mt7996/mac.h (I have not removed it).

> 
> I'm thinking if it's too early to create this patch for just moving
> mt7996 to connac3_lib?

The code I moved is used by the other device as well. This series is a
preliminary series to support it.

Regards,
Lorenzo

> 
> Ryder
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

  reply	other threads:[~2023-06-09 16:33 UTC|newest]

Thread overview: 27+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-06-09  8:15 [PATCH v2 00/15] mt76: introduce connac3_mac support Lorenzo Bianconi
2023-06-09  8:15 ` [PATCH v2 01/15] wifi: mt76: mt7915: move sta_poll_list and sta_poll_lock in mt76_dev Lorenzo Bianconi
2023-06-09  8:15 ` [PATCH v2 02/15] wifi: mt76: mt7603: rely on shared sta_poll_list and sta_poll_lock Lorenzo Bianconi
2023-06-09  8:15 ` [PATCH v2 03/15] wifi: mt76: mt7615: " Lorenzo Bianconi
2023-06-09  8:15 ` [PATCH v2 04/15] wifi: mt76: mt7996: " Lorenzo Bianconi
2023-06-09  8:15 ` [PATCH v2 05/15] wifi: mt76: mt7921: " Lorenzo Bianconi
2023-06-09  8:15 ` [PATCH v2 06/15] wifi: mt76: mt7915: move poll_list in mt76_wcid Lorenzo Bianconi
2023-06-09  8:15 ` [PATCH v2 07/15] wifi: mt76: mt7603: rely on shared poll_list field Lorenzo Bianconi
2023-06-09  8:15 ` [PATCH v2 08/15] wifi: mt76: mt7615: " Lorenzo Bianconi
2023-06-09  8:15 ` [PATCH v2 09/15] wifi: mt76: mt7996: " Lorenzo Bianconi
2023-06-09  8:15 ` [PATCH v2 10/15] wifi: mt76: mt7921: " Lorenzo Bianconi
2023-06-09  8:15 ` [PATCH v2 11/15] wifi: mt76: move ampdu_state in mt76_wcid Lorenzo Bianconi
2023-06-09  8:15 ` [PATCH v2 12/15] mt76: connac: move more mt7921/mt7915 mac shared code in connac lib Lorenzo Bianconi
2023-06-09  8:15 ` [PATCH v2 13/15] wifi: mt76: move rate info in mt76_vif Lorenzo Bianconi
2023-06-09  8:15 ` [PATCH v2 14/15] wifi: mt76: connac: move connac3 definitions in mt76_connac3_mac.h Lorenzo Bianconi
2023-06-09  8:15 ` [PATCH v2 15/15] wifi: mt76: connac: add connac3 mac library Lorenzo Bianconi
2023-06-09 16:15   ` Ryder Lee
2023-06-09 16:34     ` lorenzo.bianconi [this message]
2023-06-09 16:38       ` Ryder Lee
2023-06-09 16:45         ` lorenzo
     [not found]           ` <PH8PR12MB7230DC95457EF9C667E679AEAC51A@PH8PR12MB7230.namprd12.prod.outlook.com>
     [not found]             ` <3cae89c45b0f17eaab20876eb50572ef202f92bc.camel@mediatek.com>
2023-06-09 19:04               ` shayne.chen
2023-06-10 10:06                 ` lorenzo
2023-06-10 13:49                   ` shayne.chen
2023-06-10 14:59                     ` Ryder Lee
2023-06-10  2:22           ` Deren Wu (武德仁)
2023-06-10  7:49             ` lorenzo
2023-06-12  8:58               ` Kalle Valo

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=ZINUnqkElqSOITxT@localhost.localdomain \
    --to=lorenzo.bianconi@redhat.com \
    --cc=Ryder.Lee@mediatek.com \
    --cc=linux-wireless@vger.kernel.org \
    --cc=lorenzo@kernel.org \
    --cc=nbd@nbd.name \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).