diff --git a/src/jaguar3/FrameParserJaguar3.h b/src/jaguar3/FrameParserJaguar3.h index 44d4baa..2e2c36f 100644 --- a/src/jaguar3/FrameParserJaguar3.h +++ b/src/jaguar3/FrameParserJaguar3.h @@ -38,6 +38,7 @@ constexpr size_t RXDESC_SIZE_8822C = 24; /* RX_DESC_SIZE_88XX */ #define SET_TX_DESC_RATE_ID_8822C(d, v) SET_BITS_TO_LE_4BYTE((d) + 0x04, 16, 5, v) #define SET_TX_DESC_PKT_OFFSET_8822C(d, v) SET_BITS_TO_LE_4BYTE((d) + 0x04, 24, 5, v) #define SET_TX_DESC_USE_RATE_8822C(d, v) SET_BITS_TO_LE_4BYTE((d) + 0x0C, 8, 1, v) +#define SET_TX_DESC_DISRTSFB_8822C(d, v) SET_BITS_TO_LE_4BYTE((d) + 0x0C, 9, 1, v) #define SET_TX_DESC_DISDATAFB_8822C(d, v) SET_BITS_TO_LE_4BYTE((d) + 0x0C, 10, 1, v) #define SET_TX_DESC_NAVUSEHDR_8822C(d, v) SET_BITS_TO_LE_4BYTE((d) + 0x0C, 15, 1, v) #define SET_TX_DESC_NDPA_8822C(d, v) SET_BITS_TO_LE_4BYTE((d) + 0x0C, 22, 2, v) @@ -82,6 +83,7 @@ constexpr size_t RXDESC_SIZE_8822C = 24; /* RX_DESC_SIZE_88XX */ * cal_txdesc_chksum_8822c. */ #define SET_TX_DESC_DMA_TXAGG_NUM_8822C(d, v) SET_BITS_TO_LE_4BYTE((d) + 0x1C, 24, 8, v) #define SET_TX_DESC_EN_HWSEQ_8822C(d, v) SET_BITS_TO_LE_4BYTE((d) + 0x20, 15, 1, v) +#define SET_TX_DESC_SW_SEQ_8822C(d, v) SET_BITS_TO_LE_4BYTE((d) + 0x24, 12, 12, v) #define GET_TX_DESC_PKT_OFFSET_8822C(d) LE_BITS_TO_4BYTE((d) + 0x04, 24, 5) /* --- RX descriptor fields --- */ @@ -125,38 +127,35 @@ inline void cal_txdesc_chksum_8822c(uint8_t *txdesc) { /* Fill an 8822C data/monitor-inject TX descriptor (48 bytes, zeroed by caller) * and finalise its checksum. `bw` is the descriptor BW code (0=20,1=40,2=80), * `rate_hw` the DESC_RATE* index (MRateToHwRate output), `rate_id` 8(HT)/9(VHT). - * Field choices mirror the Jaguar1 monitor-inject path (MACID 1, USE_RATE, - * DISDATAFB, HW sequence). */ + * Field choices mirror SVPcom rtl8812eu's RTL8822E monitor-inject path + * (MACID 1, USE_RATE, DISRTSFB, DISDATAFB, supplied 802.11 sequence). */ inline void fill_data_tx_desc_8822c(uint8_t *d, uint16_t pkt_size, uint8_t rate_hw, uint8_t rate_id, uint8_t bw, bool short_gi, bool ldpc, uint8_t stbc, bool bmc = false, bool ndpa = false, uint8_t data_sc = 0, uint8_t pwr_ofset_type = 0, - uint8_t pkt_offset = 0) { + uint8_t pkt_offset = 0, + bool retry = false, + uint16_t seqnum = 0) { SET_TX_DESC_TXPKTSIZE_8822C(d, pkt_size); SET_TX_DESC_OFFSET_8822C(d, static_cast(TXDESC_SIZE_8822C)); SET_TX_DESC_LS_8822C(d, 1); /* Broadcast/multicast marker: the kernel sets this whenever addr1 is a group * address, so mark group-addressed frames the same way for a faithful port. */ SET_TX_DESC_BMC_8822C(d, bmc ? 1 : 0); - /* Remaining fields mirror the kernel's 8822e data/inject descriptor so the - * on-wire TXDESC is byte-identical (verified against a usbmon capture of the - * kernel's MCS7 inject): hw-managed sequence (DISQSELSEQ), SU/broadcast group - * (G_ID=63), a bounded per-frame retry limit (RTY_LMT_EN + RTS_DATA_RTY_LMT), - * RA-group 9, and a non-zero SW_DEFINE. NB: these do not change on-air - * throughput (the chip already airs a saturating MCS7 flood at ~77% duty with - * or without them) — they are here for a faithful port, not a fix. */ - SET_TX_DESC_DISQSELSEQ_8822C(d, 1); - SET_TX_DESC_G_ID_8822C(d, 0x3f); + /* SVPcom monitor injection keeps the frame's 802.11 sequence number: + * EN_HWSEQ is clear and SW_SEQ is copied from the header. */ SET_TX_DESC_RTY_LMT_EN_8822C(d, 1); - SET_TX_DESC_RTS_DATA_RTY_LMT_8822C(d, 12); + /* RADIOTAP_F_TX_NOACK means zero MAC retries; other injected frames use + * SVPcom's six-attempt limit. */ + SET_TX_DESC_RTS_DATA_RTY_LMT_8822C(d, retry ? 6 : 0); SET_TX_DESC_MACID_8822C(d, 0x01); SET_TX_DESC_QSEL_8822C(d, 0x12); /* MGMT queue (mirrors Jaguar1 inject) */ SET_TX_DESC_RATE_ID_8822C(d, 9); /* kernel uses RA-group 9 for the inject path */ SET_TX_DESC_USE_RATE_8822C(d, 1); - SET_TX_DESC_DISDATAFB_8822C(d, 0); - SET_TX_DESC_SW_DEFINE_8822C(d, 1); + SET_TX_DESC_DISRTSFB_8822C(d, 1); + SET_TX_DESC_DISDATAFB_8822C(d, 1); SET_TX_DESC_DATARATE_8822C(d, rate_hw); SET_TX_DESC_DATA_BW_8822C(d, bw); /* Data sub-channel: which 20/40 MHz slice of a wider configured channel the @@ -170,7 +169,8 @@ inline void fill_data_tx_desc_8822c(uint8_t *d, uint16_t pkt_size, * descriptor). Inside the checksummed span (0x14) — before the checksum. */ if (pwr_ofset_type) SET_TX_DESC_TXPWR_OFSET_TYPE_8822C(d, pwr_ofset_type & 0x3); - SET_TX_DESC_EN_HWSEQ_8822C(d, 1); + SET_TX_DESC_EN_HWSEQ_8822C(d, 0); + SET_TX_DESC_SW_SEQ_8822C(d, seqnum & 0x0fff); /* USB-agg boundary shim: pkt_offset × 8 bytes of pad between this descriptor * and its frame (halmac PKT_OFFSET, unit 8 B). 0 = none (byte-identical). * MUST precede the checksum: on the 8822C the checksum span itself extends diff --git a/src/jaguar3/RtlJaguar3Device.cpp b/src/jaguar3/RtlJaguar3Device.cpp index 1fc75bf..73e4a5f 100644 --- a/src/jaguar3/RtlJaguar3Device.cpp +++ b/src/jaguar3/RtlJaguar3Device.cpp @@ -1813,6 +1813,7 @@ size_t RtlJaguar3Device::build_tx_block(const uint8_t *packet, size_t length, uint8_t fixed_rate = MGN_1M; uint8_t sgi = 0, ldpc = 0, stbc = 0; + uint16_t tx_flags = 0; ChannelWidth_t bwidth = CHANNEL_WIDTH_20; bool vht = (radiotap_length != 0x0d); bool rate_from_radiotap = false; /* did the frame's radiotap carry a rate? */ @@ -1837,6 +1838,9 @@ size_t RtlJaguar3Device::build_tx_block(const uint8_t *packet, size_t length, fixed_rate = *it.this_arg; rate_from_radiotap = true; break; + case IEEE80211_RADIOTAP_TX_FLAGS: + tx_flags = get_unaligned_le16(it.this_arg); + break; case IEEE80211_RADIOTAP_CHANNEL: /* 2 x __le16: frequency (MHz), then flags. Frequency is authoritative * for the per-packet hop target; flags are ignored (rate/BW come from @@ -1964,6 +1968,9 @@ size_t RtlJaguar3Device::build_tx_block(const uint8_t *packet, size_t length, * the kernel's descriptor for group-addressed frames. */ const uint8_t *dot11 = packet + radiotap_length; bool bmc = frame_len >= 6 && (dot11[4] & 0x01); + uint16_t seqnum = 0; + if (frame_len >= 24) + seqnum = (get_unaligned_le16(dot11 + 22) >> 4) & 0x0fff; /* STBC guard (IRtlDevice contract) — 8822C/8822E are 2T2R so this never * fires today, but keeps the invariant uniform across families: never air an * STBC frame the chip can't do. */ @@ -1975,10 +1982,14 @@ size_t RtlJaguar3Device::build_tx_block(const uint8_t *packet, size_t length, uint8_t pwr_type = _txpkt_dflt_type.load(std::memory_order_relaxed); if (radiotap_pkt_pwr_db != INT_MIN) pwr_type = txpkt_type_for_idx(txpkt_idx_for_qdb(radiotap_pkt_pwr_db * 4)); + /* SVPcom monitor injection maps RADIOTAP_F_TX_NOACK to no MAC retry. + * wfb-ng sets this for raw-link packets, avoiding a repeated TX burst. */ + constexpr uint16_t kRadiotapTxNoAck = 0x0008; + const bool retry = (tx_flags & kRadiotapTxNoAck) == 0; jaguar3::fill_data_tx_desc_8822c( out, static_cast(frame_len), MRateToHwRate(fixed_rate), rate_id, bw_desc, sgi != 0, ldpc != 0, stbc, bmc, ndpa, data_sc, pwr_type, - pkt_offset); + pkt_offset, retry, seqnum); if (_cfg.tx.report) { /* DEVOURER_TX_REPORT: SPE_RPT asks the fw for a per-frame CCX TX report; * the report echoes SW_DEFINE's low byte, so stamp a rotating tag for diff --git a/tests/txagg_selftest.cpp b/tests/txagg_selftest.cpp index 962cc91..4701518 100644 --- a/tests/txagg_selftest.cpp +++ b/tests/txagg_selftest.cpp @@ -220,7 +220,21 @@ static void test_desc_8822c() { jaguar3::fill_data_tx_desc_8822c(blk, 1500, 7, 9, 0, false, false, 0, /*bmc=*/false, /*ndpa=*/false, /*data_sc=*/0, /*pwr_ofset_type=*/0, - /*pkt_offset=*/1); + /*pkt_offset=*/1, /*retry=*/false, + /*seqnum=*/0xabc); + CHECK((blk[0x0d] & 0x06) == 0x06, + "8822e: forced-rate fallback bits=0x%02x want DISRTSFB|DISDATAFB", + blk[0x0d] & 0x06); + CHECK((blk[0x12] & 0x02) != 0 && (blk[0x12] & 0xfc) == 0, + "8822e: TX_NOACK retry descriptor byte=0x%02x want enable/limit=0", + blk[0x12]); + CHECK((blk[0x21] & 0x80) == 0 && + LE_BITS_TO_4BYTE(blk + 0x24, 12, 12) == 0xabc, + "8822e: SW sequence mismatch (hwseq=%u swseq=%u)", + (blk[0x21] >> 7) & 1, LE_BITS_TO_4BYTE(blk + 0x24, 12, 12)); + CHECK(LE_BITS_TO_4BYTE(blk + 0x08, 24, 6) == 0 && + LE_BITS_TO_4BYTE(blk + 0x18, 0, 12) == 0, + "8822e: injected frame must leave G_ID/SW_DEFINE at the SVP defaults"); CHECK((blk[0x07] & 0x1f) == 1, "8822c: pkt_offset field=%u want 1", blk[0x07] & 0x1f); const uint16_t ck_pad0 = static_cast(blk[0x1C] | (blk[0x1D] << 8));