From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=209.85.221.48 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1794506207; b=VdPa2LHqHoGjxbFm6E940wgCnfnp1kgf2CxEVq/xoRa+Z7OBJ7I9LxIiRJD21OiLR81U tKfDq3ay7jpyiSzNJ+Ld1QRgzVlI3pKF6LKWCa+wV/aOr5WdaUuCSYka/VsACJr3hAKe9 ct4KZhg2wYpCRhK3ukn8lWYCngo2x0rV4OOleTzdFnpVvAyBp4Stq8mttKEdQQqCRH/Bp 0UJWmFgQrKJwwygOM+NimY6GDN9FoS+G2gfwXqvYDuJTYjjCPVtfWza6z9zASzs7DowxF m/n/pKGwOArlwL9uairXzSYpVI+3wD0I+ZIga6FtCE5OKkcJkdJgzv3sJRY3PPPDBPg== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1794506207; h=DMARC-Filter:Received:DKIM-Signature:X-Google-DKIM-Signature: X-Forwarded-Encrypted:X-Gm-Message-State:X-Gm-Gg:X-Received:Received: From:To:Cc:Subject:Date:Message-ID:X-Mailer:In-Reply-To:References: MIME-Version:Content-Transfer-Encoding; bh=HVf+U3hxUblRExsvvhERdHcHREEMt0I1/4RW7w8TQjA=; b=WHJV3w7KM1a6BEawzbJqWOPnGFqQYTLKLPMg5rDFl20G9e6/dedIKOp2a3kiZU/FnbiV 1/YuUI4GObT2heLtxWrsu6XQ/UHgDtbdAiJUZQz9Toq6to31gWzJo2ILi7JZCKlH1FMn6 CfHct8hhJU/1l1IDXl6ohh/HOjv+fDacTmzJy4UlC2abSc9c7DPnHlYLuCYlRzgPIxC+c wIPVF8j+Z7LIBhJF6UUGWTPDFsiM7N5GWMC55M66gLxTxZFl25L6Q+wdkIk/xq/yp9l5N gxhmbrxL/KWZBF/tUsiT88X+X/+SPhreDrDiL1rgJY+8J32YglWRZj44uCAYvSEOMiA== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20260707 header.b=bs0tI4Lc; arc=none smtp.remote-ip=209.85.221.48 Received: from mail-wr1-f48.google.com (mail-wr1-f48.google.com [209.85.221.48]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits) key-exchange X25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 13C7860A2626 for ; Thu, 12 Nov 2026 17:56:45 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org 13C7860A2626 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20260707 header.b=bs0tI4Lc Received: by mail-wr1-f48.google.com with SMTP id ffacd0b85a97d-491f34b4e0fso12325f8f.2 for ; Thu, 12 Nov 2026 09:56:45 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20260707; t=1794506204; x=1795111004; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=HVf+U3hxUblRExsvvhERdHcHREEMt0I1/4RW7w8TQjA=; b=bs0tI4Lchd2DkyHnjMoqg78rp6BUgfsi4omjEdhf/U/mHtF5+E/C8MwaVag1EmrtHq S5nThcoqLvtH4NUxqFR+Ysxj3tzK2CEwzRQGp++qiyu7//siMmRDhnWqI5r+Otdba8Ur FPS1WMT1l0ZeSkXmvQlOl3529LnOQAkmD7jcZQttN9ZT3v2rF9MiTbcnXPXIbW+fQw0L 7jXhKjrB1WmOm0AIoLeW7BfyjYK79Oqr5qG2Bu7A4OAgmD9MJa3D/vJ+O+o8ZZ4xKHKr CH2XZqTEpUnJcsDKySi9+UdKkAgiHI2tjMgkXnww0ynTsu100jPhKpmIXsyD41tI88dA kWXA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1794506204; x=1795111004; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=HVf+U3hxUblRExsvvhERdHcHREEMt0I1/4RW7w8TQjA=; b=heDl6rf3zKopAIHY6bK25NLJIT7pIcoykxTqkLPuYBkv4klymyXgNCzUbTbS/vQwjU NCqeuyBRX8K355Sbm9YDluLdDlUgQALjmxRbtdEcIm3ljraSaSGOrmiIgSrb9hKj9eyo GoS3YPHbLQmMt7scl2R3wBRHm0f7oY7IRhXQS1HDZdLMAIh9IXBJRI1WJc2PY+AqvqG2 pAM5nY+gqA3sQv63ZIq3UfYuXQc+PoGMqTLv+xTWDc+lcrPjdGZ7dXyoliIMt/0CZG9t H2tByZCPd1bi7x1zoiBrxYOt9EjlnqjKgWX2Ty2R4reK3I9uhjSvi/WrBgTuZTJJN/m5 pj1Q== X-Forwarded-Encrypted: i=1; AL+hErFE1YRaXaHzEsdUvJcMPAOUYVqTJdBtgvr25Sf9TxR1Mq9FYc5ZTUSmt9X9CnZXVRBe5w25lM9J0+GYdGg=@vger.kernel.org X-Gm-Message-State: APuU4Z8kxBcuC2Hkh/7Ko1W0RBi2P4LWRmsv+EBIUuAJaaM8q5ZzB2Yu kFL7ITHNslsUJ4ZnCZsrJ1enGoMQrAxMhDUo8nO7vhijTMsRwCWV30t8WLgjmSafSY8= X-Gm-Gg: AYBFou1YHBi2VERIRDWwub6TJmCJf5MkrG2GYHJZXCxCPoAlkIn3kRS5RVXbWcx/CYY g6N8ghf45wHiexykey1bj+takVZABJFupQnavCzxYs419N2edKoQDm0++zYuY0bzbVpOYH7FFcR 1nMnOhfXNTzEzCeeoizwgaWaXx0sMSM/bihi2c0GznyXknuqdgPygsl1XPYMW6ciiDQ0S784bno 6FXTVGZJNT/eB9I4d7bIBL/HnvnrpIDANi9rLZrcGs8vXPF2Q3KvN8aRKRUA7Z1ctUgizNbjmpG DjEXhJcH+Rbyc5jyLTMnX53TkpBhxP/MzkCWyXHDbI6/gFDyM9hEaljLZoXkYOIyhNfoWe/nFAB bm0zJEy5nlUZaHtCrbHM0wwpW507mzJ5NG8BLRLqAT8w4VIfZusqFfm+4NK8ocxc0ecB+AVA7qR n6c9sTq0HmEL0QNDBr1+DYr93+SoYBH4i0GtJ6RPpZqBZ8EozC8sBeZP3VNsy4LxPNuNTO7OjY+ jCpVpCVeqG3KXaNXqUr7UbkNouYFFwpoU5aGUb7QHC9jJXGXGLYKbJIhRok0ULcgCp9y5SLRo07 dbnR6O7KL2F+zfOVnXwbZnYskKUxVLY6uZNvA/xKi8rfBzxLOyvUViWFqvIr3VFe7d+/5OP8Xpr 3+mzgJHgJXNna8YGdSVR8AJ6jbQLY9pl7L6dGn/Cn1p4LCUqE4nhlTh7L8GBHJOdXnsR26i8kBZ gUAMijpxVUBsSZNUgS6CQX6l1wmEwEZl4dvFUC2ejRHw== X-Received: by 2002:a5d:5f51:0:b0:495:5c83:51bc with SMTP id ffacd0b85a97d-49c6fc9e6efmr339741f8f.5.1794506204392; Thu, 12 Nov 2026 09:56:44 -0800 (PST) Received: from zitrone.tail45b538.ts.net ([2a02:810d:a1:6000:900:be5c:7a9c:c176]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-49c5f1c193fsm547430f8f.13.2026.11.12.09.56.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 12 Nov 2026 09:56:43 -0800 (PST) From: Andreas Wendleder To: linux-wireless@vger.kernel.org Cc: Johannes Berg , b43-dev@lists.infradead.org, Michael Buesch , Alessio Ferri , kvalo@kernel.org, julian.calaby@gmail.com, jpeisach@ubuntu.com, rosenp@gmail.com, linux-kernel@vger.kernel.org Subject: [PATCH RFC wireless-next 2/4] wifi: b43: TX descriptor and TX status for the AC microcode Date: Thu, 12 Nov 2026 18:56:38 +0100 Message-ID: <20261112175640.40603-3-gonsolo@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20261112175640.40603-1-gonsolo@gmail.com> References: <20261112175640.40603-1-gonsolo@gmail.com> X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: 8bit The AC microcode takes a 4 byte passthrough prefix and a 124 byte descriptor with four 20 byte rate entries. Add the descriptor builder: legacy CCK and OFDM rates, HT rates with the PLCP laid out as brcmsmac does, VHT rate blocks (frame type 3, with the width and the subband of the primary channel in the first two words and the VHT-SIG-A fields), and the cache info that session frames carry (the microcode builds the A-MPDU from them). The cache info must not carry a cipher: mac80211 has encrypted the frame already. The TX status comes as two 4 word records. The attempt count is the low byte of word 2, not the sum of its bytes, and a record with more than one MPDU carries a block ack bitmap for the consecutive cookies; the cookies are advanced with the new b43_dma_cookie_advance(). Only microcode revision 832 has been tested. Co-developed-by: Alessio Ferri Signed-off-by: Alessio Ferri Signed-off-by: Andreas Wendleder --- drivers/net/wireless/broadcom/b43/dma.c | 13 ++ drivers/net/wireless/broadcom/b43/dma.h | 1 + drivers/net/wireless/broadcom/b43/main.c | 76 ++++++++ drivers/net/wireless/broadcom/b43/xmit.c | 214 +++++++++++++++++++++++ drivers/net/wireless/broadcom/b43/xmit.h | 11 ++ 5 files changed, 315 insertions(+) diff --git a/drivers/net/wireless/broadcom/b43/dma.c b/drivers/net/wireless/broadcom/b43/dma.c index 5f968b0..404f75f 100644 --- a/drivers/net/wireless/broadcom/b43/dma.c +++ b/drivers/net/wireless/broadcom/b43/dma.c @@ -1444,6 +1444,19 @@ out: return err; } +/* The cookie of the frame @n frames after @cookie on the same ring. */ +u16 b43_dma_cookie_advance(struct b43_wldev *dev, u16 cookie, unsigned int n) +{ + struct b43_dmaring *ring; + int slot; + + ring = parse_cookie(dev, cookie, &slot); + if (!ring) + return cookie; + return generate_cookie(ring, (slot + n * TX_SLOTS_PER_FRAME) % + ring->nr_slots); +} + void b43_dma_handle_txstatus(struct b43_wldev *dev, const struct b43_txstatus *status) { diff --git a/drivers/net/wireless/broadcom/b43/dma.h b/drivers/net/wireless/broadcom/b43/dma.h index 14de4cb..5c84b1f 100644 --- a/drivers/net/wireless/broadcom/b43/dma.h +++ b/drivers/net/wireless/broadcom/b43/dma.h @@ -298,6 +298,7 @@ void b43_dma_tx_resume(struct b43_wldev *dev); int b43_dma_tx(struct b43_wldev *dev, struct sk_buff *skb); +u16 b43_dma_cookie_advance(struct b43_wldev *dev, u16 cookie, unsigned int n); void b43_dma_handle_txstatus(struct b43_wldev *dev, const struct b43_txstatus *status); diff --git a/drivers/net/wireless/broadcom/b43/main.c b/drivers/net/wireless/broadcom/b43/main.c index da7e420..5830acb 100644 --- a/drivers/net/wireless/broadcom/b43/main.c +++ b/drivers/net/wireless/broadcom/b43/main.c @@ -1414,6 +1414,74 @@ static void handle_irq_transmit_status(struct b43_wldev *dev) break; v1 = b43_read32(dev, B43_MMIO_XMITSTAT_1); + if (b43_ucode_is_ac(dev)) { + /* rev 40+ ucode: two 4-word records per frame */ + u32 v2 = b43_read32(dev, B43_MMIO_XMITSTAT_0 + 8); + u32 v3 = b43_read32(dev, B43_MMIO_XMITSTAT_0 + 12); + unsigned int n; + + /* + * second package: words 1 and 2 are the block ack + * bitmap + */ + u32 w0 = b43_read32(dev, B43_MMIO_XMITSTAT_0); + u32 w1 = b43_read32(dev, B43_MMIO_XMITSTAT_1); + u32 w2 = b43_read32(dev, B43_MMIO_XMITSTAT_0 + 8); + u32 w3 = b43_read32(dev, B43_MMIO_XMITSTAT_0 + 12); + unsigned int mpdus = (v0 >> 8) & 0x7f, i; + u64 bitmap = ((u64)w2 << 32) | w1; + + memset(&stat, 0, sizeof(stat)); + stat.cookie = v0 >> 16; + stat.seq = v1 & 0xffff; + stat.phy_stat = (v1 >> 16) & 0xff; + stat.intermediate = !!(v0 & 0x0004); + stat.pm_indicated = !!(v0 & 0x0008); + stat.supp_reason = (v0 & 0x00f0) >> 4; + stat.acked = !!(v0 & 0x8000); + /* + * : the attempt count is the LOW BYTE of word 2 only. + * Bits 16-23 hold a constant 1 on every acknowledged + * frame (0 on suppressed ones), not a second attempt + * counter: summing it made every frame look like it + * needed a retry, so minstrel saw ~50 % failures at + * every rate and TX stayed at 1-6 Mbit/s (1.2 "retries" + * per frame regardless of rate or TX power). + */ + n = v2 & 0xff; + stat.frame_count = min(n, 15u); + stat.fb_count = min((v2 >> 16) & 0xff, 15u); + b43dbg(dev->wl, + "AC txstatus %08x %08x %08x %08x / %08x %08x %08x %08x\n", + v0, v1, v2, v3, w0, w1, w2, w3); + if (mpdus > 1) { + /* + * A-MPDU: one status for @mpdus frames with + * consecutive cookies + */ + u16 cookie = stat.cookie; + + /* + * the attempt count covers the whole aggregate + */ + stat.frame_count = + min(DIV_ROUND_UP(n, mpdus), 15u); + stat.fb_count = + min(DIV_ROUND_UP((v2 >> 16) & 0xff, + mpdus), 15u); + for (i = 0; i < mpdus; i++) { + stat.cookie = cookie; + stat.acked = !!(bitmap & (1ULL << i)); + b43_handle_txstatus(dev, &stat); + cookie = b43_dma_cookie_advance( + dev, cookie, 1); + } + continue; + } + b43_handle_txstatus(dev, &stat); + continue; + } + stat.cookie = (v0 >> 16); stat.seq = (v1 & 0x0000FFFF); stat.phy_stat = ((v1 & 0x00FF0000) >> 16); @@ -1444,6 +1512,14 @@ static void drain_txstatus_queue(struct b43_wldev *dev) if (!(dummy & 0x00000001)) break; dummy = b43_read32(dev, B43_MMIO_XMITSTAT_1); + if (b43_ucode_is_ac(dev)) { + int i; + + b43_read32(dev, B43_MMIO_XMITSTAT_0 + 8); + b43_read32(dev, B43_MMIO_XMITSTAT_0 + 12); + for (i = 0; i < 4; i++) + b43_read32(dev, B43_MMIO_XMITSTAT_0 + 4 * i); + } } } diff --git a/drivers/net/wireless/broadcom/b43/xmit.c b/drivers/net/wireless/broadcom/b43/xmit.c index f0b0825..cf9e7a5 100644 --- a/drivers/net/wireless/broadcom/b43/xmit.c +++ b/drivers/net/wireless/broadcom/b43/xmit.c @@ -231,6 +231,216 @@ static u8 b43_calc_fallback_rate(u8 bitrate, int gmode) return 0; } +/* + * One VHT rate block (frame type 3), as the stock driver lays it out and + * Alessio Ferri's driver builds it: the width in word 0 bits 15:14, the + * subband of the primary within that width in word 1, MCS and NSS - 1 in + * word 2, VHT-SIG-A1/A2 in the PLCP bytes (group ID 63, both reserved bits + * set), FBW 0x0300 and the rate in 500 kbit/s. + */ +static void b43_txhdr_ac_vht_block(u8 *ri, const struct ieee80211_tx_rate *t, + u16 chanspec) +{ + static const u8 rate_bw[] = { + RATE_INFO_BW_20, RATE_INFO_BW_40, RATE_INFO_BW_80, + }; + u8 mcs = ieee80211_rate_get_vht_mcs(t); + u8 nss = ieee80211_rate_get_vht_nss(t); + unsigned int bw = 0; + struct rate_info rinfo = { .flags = RATE_INFO_FLAGS_VHT_MCS }; + u16 sb = 0; + u32 a1, a2; + + /* wider than 20 MHz only inside the 80 MHz channel */ + if ((chanspec & 0x3800) == 0x2000) { + if (t->flags & IEEE80211_TX_RC_80_MHZ_WIDTH) + bw = 2; + else if (t->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + bw = 1; + sb = ((chanspec >> 8) & 0x7) >> bw; + } + rinfo.mcs = mcs; + rinfo.nss = nss; + rinfo.bw = rate_bw[bw]; + + put_unaligned_le16(3 | 0x0004 | + ((nss > 1 ? 3 : 1) << 6) | + (bw << 14), ri + 0x00); + put_unaligned_le16(sb, ri + 0x02); + put_unaligned_le16((mcs & 0xf) | ((nss - 1) << 4), ri + 0x04); + a1 = bw | BIT(2) | (63 << 4) | ((nss - 1) << 10) | BIT(23); + a2 = (mcs << 4) | BIT(9); + ri[0x06] = a1; + ri[0x07] = a1 >> 8; + ri[0x08] = a1 >> 16; + ri[0x09] = a2; + ri[0x0a] = a2 >> 8; + ri[0x0b] = a2 >> 16; + put_unaligned_le16(0x0300, ri + 0x0c); + put_unaligned_le16(cfg80211_calculate_bitrate(&rinfo) / 5, ri + 0x0e); +} + +static int b43_generate_txhdr_ac(struct b43_wldev *dev, u8 *h, + struct sk_buff *skb, + struct ieee80211_tx_info *info, u16 cookie) +{ + static const u8 cck[] = { B43_CCK_RATE_1MB, B43_CCK_RATE_2MB, + B43_CCK_RATE_5MB, B43_CCK_RATE_11MB }; + static const u8 ofdm[] = { B43_OFDM_RATE_6MB, B43_OFDM_RATE_9MB, + B43_OFDM_RATE_12MB, B43_OFDM_RATE_18MB, + B43_OFDM_RATE_24MB, B43_OFDM_RATE_36MB, + B43_OFDM_RATE_48MB, B43_OFDM_RATE_54MB }; + const struct ieee80211_hdr *wlhdr = (const void *)skb->data; + struct ieee80211_rate *txrate = + ieee80211_get_tx_rate(dev->wl->hw, info); + u8 rate = txrate ? txrate->hw_value : B43_CCK_RATE_1MB; + bool is_ofdm = b43_is_ofdm_rate(rate); + unsigned int len = skb->len + FCS_LEN; + u8 *ri = h + B43_TXH_AC_RATE(0); + u16 mac_lo = 0, phy0, idx = 0, chanspec, subband; + int ht_mcs = -1; + /* Only HT rates go out as session frames: a legacy probe frame with the + * aggregate header fails at every attempt. + */ + bool vht = info->control.rates[0].flags & IEEE80211_TX_RC_VHT_MCS; + bool agg = (info->flags & IEEE80211_TX_CTL_AMPDU) && + (info->control.rates[0].flags & + (IEEE80211_TX_RC_MCS | IEEE80211_TX_RC_VHT_MCS)); + const u8 *tbl = is_ofdm ? ofdm : cck; + unsigned int n = is_ofdm ? ARRAY_SIZE(ofdm) : ARRAY_SIZE(cck); + + /* Hardware crypto needs the AC key fields; load with nohwcrypt=1. */ + if (info->control.hw_key) + return -EOPNOTSUPP; + + memset(h, 0, B43_TXH_AC_LEN); + h[0] = 0x02; /* passthrough prefix, as wl sends it */ + h[2] = 0x02; + h += B43_TXH_AC_PREFIX; + ri = h + B43_TXH_AC_RATE(0); + while (idx < n && tbl[idx] != rate) + idx++; + if (idx == n) + idx = 0; + + if (!is_multicast_ether_addr(wlhdr->addr1) && + !(info->flags & IEEE80211_TX_CTL_NO_ACK)) + mac_lo |= 0x0080; /* expect immediate ACK */ + if (!(le16_to_cpu(wlhdr->seq_ctrl) & IEEE80211_SCTL_FRAG)) + mac_lo |= 0x4000; /* start of MSDU */ + if (ieee80211_is_beacon(wlhdr->frame_control)) + mac_lo |= 0x0200; /* ignore PMQ */ + + chanspec = b43_ac_chanspec(dev, dev->phy.channel); + /* A 20 MHz frame inside an 80 MHz channel names its subband (word 1) */ + subband = (chanspec & 0x3800) == 0x2000 ? (chanspec >> 8) & 0x7 : 0; + + if (agg) { + /* MPDU of a block ack session, as the stock driver posts it + * (rxtx-1s-ht20-40-80.zip): MAC control 0x45c0, no fixed rate, + * the sequence number, and the cache info at +0x64: no cipher + * (we encrypt in software; the stock 50 04 makes the microcode + * encrypt again), MPDUs per aggregate at the primary and the + * fallback rate, duration 5414 us, window 63, max length 0x14. + * Without the cache info the AP deauthenticates us. + */ + static const u8 cache[8] = { 0x00, 0x00, 0x04, 0x04, + 0x26, 0x15, 0x3f, 0x14 }; + + mac_lo = 0x45c0; + memcpy(h + 0x64, cache, sizeof(cache)); + } + put_unaligned_le16(mac_lo, h + 0x02); + put_unaligned_le16(agg ? 0 : 0x0002, h + 0x04); /* fixed rate */ + put_unaligned_le16(chanspec, h + 0x06); + h[0x08] = ieee80211_hdrlen(wlhdr->frame_control); + put_unaligned_le16(len, h + 0x0a); + put_unaligned_le16(cookie, h + 0x0c); + if (agg) + put_unaligned_le16(le16_to_cpu(wlhdr->seq_ctrl), h + 0x0e); + + /* Rate entry 0: frame type, always-set bit 2, TX core 0 mask. */ + phy0 = (is_ofdm ? 1 : 0) | 0x0004 | (1 << 6); + if (!is_ofdm && rate != B43_CCK_RATE_1MB && + (info->control.rates[0].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE)) + phy0 |= 0x0010; + if (info->control.rates[0].flags & IEEE80211_TX_RC_MCS) { + ht_mcs = info->control.rates[0].idx; + } else if (vht && !agg) { + /* + * A VHT frame always goes out as an A-MPDU; outside a session + * send the HT20 rate of the same stream count instead. + */ + const struct ieee80211_tx_rate *r = &info->control.rates[0]; + + ht_mcs = min_t(int, ieee80211_rate_get_vht_mcs(r), 7) + + 8 * (ieee80211_rate_get_vht_nss(r) - 1); + } + + if (vht && agg) { + b43_txhdr_ac_vht_block(ri, &info->control.rates[0], chanspec); + } else if (ht_mcs >= 0 && ht_mcs <= 15) { + /* + * HT20: frame type 2, PLCP as brcmsmac lays it out, rate + * index = MCS + */ + static const u8 ht20_500k[8] = { 13, 26, 39, 52, + 78, 104, 117, 130 }; + u8 mcs = ht_mcs; + u8 *plcp = ri + 0x06; + + phy0 = 2 | 0x0004 | ((mcs > 7 ? 3 : 1) << 6); + put_unaligned_le16(phy0, ri + 0x00); + put_unaligned_le16(subband, ri + 0x02); + put_unaligned_le16(mcs, ri + 0x04); + plcp[0] = mcs; + plcp[1] = len & 0xff; + plcp[2] = len >> 8; + /* smoothing, not sounding, reserved (brcmsmac) */ + plcp[3] = 0x07; + plcp[4] = 0; + plcp[5] = 0; + put_unaligned_le16(ht20_500k[mcs & 7] * (mcs > 7 ? 2 : 1), + ri + 0x0e); + } else { + put_unaligned_le16(phy0, ri + 0x00); + put_unaligned_le16(subband, ri + 0x02); + put_unaligned_le16(idx, ri + 0x04); + b43_generate_plcp_hdr((struct b43_plcp_hdr4 *)(ri + 0x06), + len, rate); + put_unaligned_le16(rate, ri + 0x0e); /* 500 kbit/s units */ + } + if (vht && agg && info->control.rates[1].idx >= 0 && + (info->control.rates[1].flags & IEEE80211_TX_RC_VHT_MCS)) { + put_unaligned_le16(0, ri + 0x10); + ri += 0x14; + b43_txhdr_ac_vht_block(ri, &info->control.rates[1], chanspec); + } else if (agg && !vht && info->control.rates[1].idx >= 0 && + info->control.rates[1].idx <= 15 && + (info->control.rates[1].flags & IEEE80211_TX_RC_MCS)) { + /* Second rate block: the fallback rate of the session frame */ + static const u8 ht20_500k[8] = { 13, 26, 39, 52, + 78, 104, 117, 130 }; + u8 mcs = info->control.rates[1].idx; + + put_unaligned_le16(0, ri + 0x10); + ri += 0x14; + put_unaligned_le16(2 | 0x0004 | ((mcs > 7 ? 3 : 1) << 6), + ri + 0x00); + put_unaligned_le16(subband, ri + 0x02); + put_unaligned_le16(mcs, ri + 0x04); + ri[0x06] = mcs; + ri[0x07] = len & 0xff; + ri[0x08] = len >> 8; + ri[0x09] = 0x07; + put_unaligned_le16(ht20_500k[mcs & 7] * (mcs > 7 ? 2 : 1), + ri + 0x0e); + } + put_unaligned_le16(0x0020, ri + 0x10); /* last rate entry */ + + return 0; +} + /* Generate a TX data header. */ int b43_generate_txhdr(struct b43_wldev *dev, u8 *_txhdr, @@ -259,6 +469,10 @@ int b43_generate_txhdr(struct b43_wldev *dev, struct ieee80211_rate *txrate; struct ieee80211_tx_rate *rates; + if (b43_ucode_is_ac(dev)) + return b43_generate_txhdr_ac(dev, _txhdr, skb_frag, info, + cookie); + memset(txhdr, 0, sizeof(*txhdr)); txrate = ieee80211_get_tx_rate(dev->wl->hw, info); diff --git a/drivers/net/wireless/broadcom/b43/xmit.h b/drivers/net/wireless/broadcom/b43/xmit.h index 6524a75..d403a28 100644 --- a/drivers/net/wireless/broadcom/b43/xmit.h +++ b/drivers/net/wireless/broadcom/b43/xmit.h @@ -188,9 +188,19 @@ struct b43_tx_legacy_rate_phy_ctl_entry { #define B43_TXH_PHY1_MODUL_QAM256 0x2000 /* QAM256 */ +/* + * rev 40+ (AC) TX buffer: 4-byte TX offload passthrough prefix, then the + * 124-byte descriptor (packet info + 4 rate entries + cache). + */ +#define B43_TXH_AC_PREFIX 4 +#define B43_TXH_AC_LEN (B43_TXH_AC_PREFIX + 124) +#define B43_TXH_AC_RATE(i) (0x14 + (i) * 0x14) + static inline size_t b43_txhdr_size(struct b43_wldev *dev) { + if (b43_ucode_is_ac(dev)) + return B43_TXH_AC_LEN; switch (dev->fw.hdr_format) { case B43_FW_HDR_598: return 112 + sizeof(struct b43_plcp_hdr6); @@ -214,6 +224,7 @@ struct b43_txstatus { u16 seq; /* Sequence number */ u8 phy_stat; /* PHY TX status */ u8 frame_count; /* Frame transmit count */ + u8 fb_count; /* AC: attempts at the fallback rate */ u8 rts_count; /* RTS transmit count */ u8 supp_reason; /* Suppression reason */ /* flags */