Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F170826928
D59205.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
122 KB
Referenced Files
None
Subscribers
None
D59205.diff
View Options
diff --git a/share/man/man4/rtwn.4 b/share/man/man4/rtwn.4
--- a/share/man/man4/rtwn.4
+++ b/share/man/man4/rtwn.4
@@ -19,7 +19,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd November 10, 2024
+.Dd September 1, 2026
.Dt RTWN 4
.Os
.Sh NAME
@@ -93,6 +93,8 @@
.It
Realtek 802.11n wireless 8192e (RTL8192E)
.It
+Realtek 802.11n wireless 8723b (RTL8723B)
+.It
Realtek 802.11ac wireless 8812a (RTL8812A)
.It
Realtek 802.11ac wireless 8821a (RTL8821A)
@@ -122,6 +124,7 @@
.It Pa /boot/kernel/rtwn-rtl8192cfwT.ko
.It Pa /boot/kernel/rtwn-rtl8192cfwU.ko
.It Pa /boot/kernel/rtwn-rtl8192eufw.ko
+.It Pa /boot/kernel/rtwn-rtl8723bufw.ko
.It Pa /boot/kernel/rtwn-rtl8812aufw.ko
.It Pa /boot/kernel/rtwn-rtl8821aufw.ko
.El
@@ -266,3 +269,6 @@
The
.Nm
driver currently does not implement firmware-based rate control.
+The RTL8723BU seems to depend on some firmware commands for antenna
+setup.
+Hence, it does not work without firmware.
diff --git a/share/man/man4/rtwn_usb.4 b/share/man/man4/rtwn_usb.4
--- a/share/man/man4/rtwn_usb.4
+++ b/share/man/man4/rtwn_usb.4
@@ -29,7 +29,7 @@
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
.\" THE POSSIBILITY OF SUCH DAMAGES.
.\"
-.Dd November 10, 2024
+.Dd September 1, 2026
.Dt RTWN_USB 4
.Os
.Sh NAME
@@ -59,7 +59,7 @@
The
.Nm
driver supports USB wireless network adapters based on certain
-Realtek RTL 8188/8192/8812 and 8821 chipsets, including:
+Realtek RTL 8188/8192/8723/8812 and 8821 chipsets, including:
.Pp
.Bl -column -compact "Belkin F7D1102 Surf Wireless Micro" "RTL8188CUS" "USB 2.0"
.It Em Card Ta Em Chip Ta Em Bus
@@ -97,6 +97,7 @@
.It "Netgear WNA1000M" Ta RTL8188CUS Ta USB 2.0
.It "Mercusys MW150US" Ta RTL8188EU Ta USB 2.0
.It "Planex GW-900D" Ta RTL8812AU Ta USB 3.0
+.It "Realtek RTL8723BU" Ta RTL8723BU Ta USB 2.0
.It "Realtek RTL8192CU" Ta RTL8192CU Ta USB 2.0
.It "Realtek RTL8188CUS" Ta RTL8188CUS Ta USB 2.0
.It "Sitecom WLA-7100" Ta RTL8812AU Ta USB 3.0
diff --git a/sys/conf/files b/sys/conf/files
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -2990,6 +2990,17 @@
dev/rtwn/rtl8192e/r92e_rx.c optional rtwn
dev/rtwn/rtl8192e/usb/r92eu_attach.c optional rtwn_usb
dev/rtwn/rtl8192e/usb/r92eu_init.c optional rtwn_usb
+# RTL8723B
+dev/rtwn/rtl8723b/r23b_calib.c optional rtwn
+dev/rtwn/rtl8723b/r23b_fw.c optional rtwn
+dev/rtwn/rtl8723b/r23b_init.c optional rtwn
+dev/rtwn/rtl8723b/r23b_led.c optional rtwn
+dev/rtwn/rtl8723b/r23b_rf.c optional rtwn
+dev/rtwn/rtl8723b/r23b_rom.c optional rtwn
+dev/rtwn/rtl8723b/r23b_rx.c optional rtwn
+dev/rtwn/rtl8723b/usb/r23bu_attach.c optional rtwn_usb
+dev/rtwn/rtl8723b/usb/r23bu_init.c optional rtwn_usb
+dev/rtwn/rtl8723b/usb/r23bu_rom.c optional rtwn_usb
# RTL8812A
dev/rtwn/rtl8812a/r12a_beacon.c optional rtwn
dev/rtwn/rtl8812a/r12a_calib.c optional rtwn
diff --git a/sys/contrib/dev/rtwn/rtwn-rtl8723bufw.fw.uu b/sys/contrib/dev/rtwn/rtwn-rtl8723bufw.fw.uu
new file mode 100644
--- /dev/null
+++ b/sys/contrib/dev/rtwn/rtwn-rtl8723bufw.fw.uu
@@ -0,0 +1,717 @@
+begin 644 rtwn-rtl8723bufw.fw.uu
+M`5,0`",````#)A0U3'T``.4<```````````````````"1<X"=^L`````````
+M`````````GD&``````````````````)X'P``````````````````````````
+M`````````````GA&```````"?^<```````*'WP`$#`0("`H(`P,`!`D'`P,`
+M!`@&`P(`!`@%`P$`!`T*!P4`"`P*!P0`"`L*!@4`"`L*!0,`"`L*`P(`"!02
+M#`0`$!02"00`$"0B'!(`("0B&`P`("0B%`8`("0B#P0`("0A"@0`(",A#`0`
+M(",?"@0`("(?#P0`("$?%@P`(#$O(!0`,#$O&!``,#$L&`P`,#$J%`P`,#$H
+M%```,#$D%```,#$>%```,!7P_P\````5\`\`````!?#_#P````7P#P`````0
+M\/\/````$/`/`````/4/``````#P#P``````#0```````````````````"0F
+M*@```!\A)2<H``0`!``(`!``&``D`#``2`!@`)``P`#8`#P`9`!X`*``\`%`
+M`9`!X``"``(`!``(``P`$@`8`"0`,`!(`&``;``4`#(`/`!0`'@`H`#(`/`"
+M!`8("@P0&"`P0%`!`0$"`0(#`P0$!04"!`8'!P@("`$!`0$!`@,$!08'"`$"
+M`P0%!@<(&08$`@`8``0,!`@(!`0$!00$!0<'!P@*!`<*#A$3%!4$!`0%!P<)
+M"0P.$!(%!@<-$!$2$@``````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````,*O@/XR$D*4A=`+==`(JN#"C.6*)&?UBN6,-'GUC-*,
+M["2'^.:\`@)T_\.5@;1``$#.>0-X@!;F"'`+PJ_F,.$#1!CVTJ\(V>WJB]`B
+MY0S_(R2!^`\("+\#!'\`>('F,.3R`.4,PY]0(`4,=(8E#/CF_::!".:N#+X"
+M`G3_S?CH;6#@".;`X(#VY0S3GT`GY0PDA_CFK@R^`@)T__T8YLWXY8%M8`;0
+MX/88@/7E#"2&R/85#(#3Y0PC)('X?P3"K^8PX`,0X@Q_`##A!S#C!'\(5/14
+M?,;2KU2`0@<B>(:F@70"8`;_"';_W_M_`^1X@/8(]@C?^GB!=C"01G%T`9/`
+MX.23P.!#B0%UBF!UC'G2C-*O(@+OTY0"0`-__R)T@2\O^.8@Y?3"K^9$,/;2
+MKZX,[L.?4"$.=(8N^.;Y".88O@("=/_][6E@"0GG&1GW"0F`\Q86@-KNTY]`
+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
+7[T2`_R(O^.;^[?1>(A)&J^]$0/TB1,<`
+`
+end
diff --git a/sys/dev/rtwn/rtl8723b/r23b.h b/sys/dev/rtwn/rtl8723b/r23b.h
new file mode 100644
--- /dev/null
+++ b/sys/dev/rtwn/rtl8723b/r23b.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2026 Ahmad Khalifa <vexeduxr@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef RTL8723B_H
+#define RTL8723B_H
+
+/*
+ * Global definitions.
+ */
+#define R23B_PUBQ_NPAGES 231
+#define R23B_HPQ_NPAGES 12
+#define R23B_LPQ_NPAGES 2
+#define R23B_NPQ_NPAGES 2
+
+#define R23B_TX_PAGE_COUNT 247
+
+#define R23B_RX_DMA_BUFFER_SIZE 0x3f80
+
+#define R23B_CALIB_THRESHOLD 7
+
+/*
+ * Function declarations.
+ */
+
+/* r23b_calib.c */
+void r23b_iq_calib(struct rtwn_softc *);
+void r23b_lc_calib(struct rtwn_softc *);
+void r23b_temp_measure(struct rtwn_softc *);
+
+/* r23b_fw.c */
+#ifndef RTWN_WITHOUT_UCODE
+void r23b_fw_download_enable(struct rtwn_softc *, int);
+void r23b_fw_reset(struct rtwn_softc *, int);
+#endif
+
+/* r23b_init.c */
+void r23b_init_antsel(struct rtwn_softc *);
+void r23b_init_bb_common(struct rtwn_softc *);
+void r23b_init_rf(struct rtwn_softc *);
+void r23b_post_init(struct rtwn_softc *);
+int r23b_set_page_size(struct rtwn_softc *);
+
+/* r23b_led.c */
+void r23b_set_led(struct rtwn_softc *, int, int);
+
+/* r23b_rf.c */
+void r23b_rf_write(struct rtwn_softc *, int, uint8_t, uint32_t);
+
+/* r23b_rom.c */
+int r23b_efuse_preread(struct rtwn_softc *);
+void r23b_parse_rom(struct rtwn_softc *, uint8_t *);
+
+/* r23b_rx.c */
+int8_t r23b_get_rssi_cck(struct rtwn_softc *, void *);
+
+#endif /* RTL8723B_H */
diff --git a/sys/dev/rtwn/rtl8723b/r23b_calib.c b/sys/dev/rtwn/rtl8723b/r23b_calib.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/rtwn/rtl8723b/r23b_calib.c
@@ -0,0 +1,620 @@
+/*
+ * Copyright (c) 2026 Ahmad Khalifa <vexeduxr@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/param.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+#include <sys/taskqueue.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/linker.h>
+
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <net/if_media.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_radiotap.h>
+
+#include <dev/rtwn/if_rtwnreg.h>
+#include <dev/rtwn/if_rtwnvar.h>
+
+#include <dev/rtwn/if_rtwn_debug.h>
+
+#include <dev/rtwn/rtl8188e/r88e.h>
+#include <dev/rtwn/rtl8188e/r88e_reg.h>
+
+#include <dev/rtwn/rtl8192c/r92c_reg.h>
+
+#include <dev/rtwn/rtl8192e/r92e_var.h>
+
+#include <dev/rtwn/rtl8723b/r23b.h>
+#include <dev/rtwn/rtl8723b/r23b_var.h>
+#include <dev/rtwn/rtl8723b/r23b_reg.h>
+#include <dev/rtwn/rtl8723b/r23b_fw_cmd.h>
+
+struct r23b_iqk_reg_vals {
+ uint32_t adaa[16];
+ uint32_t iqk_bb[9];
+ uint8_t txpause;
+ uint8_t bcn_ctrl[2];
+ uint8_t gpio_muxcfg;
+};
+
+struct r23b_iqk_result {
+ int32_t iqk_before;
+ int32_t iqk_after;
+};
+
+enum r23b_iqk_stage {
+ R23B_IQK_TX,
+ R23B_IQK_TX_RX,
+ R23B_IQK_RX
+};
+
+static const uint16_t r23b_iq_calib_reg_adaa[16] = {
+ 0x85c, 0xe6c, 0xe70, 0xe74,
+ 0xe78, 0xe7c, 0xe80, 0xe84,
+ 0xe88, 0xe8c, 0xed0, 0xed4,
+ 0xed8, 0xedc, 0xee0, 0xeec
+};
+
+static const uint16_t r23b_iq_calib_reg_iqk_bb[9] = {
+ 0xc04, 0xc08, 0x874, 0xb68,
+ 0xb6c, 0x870, 0x860, 0x864,
+ 0xa04
+};
+
+#define R23B_IQK_MAX_TOLERANCE 5
+#define R23B_IQK_SIGN(r) (((r) & 0x200) ? (0xfffffc00 | (r)) : (r))
+
+/* XXX
+ * Same as r92c_lc_calib except for LDO on and LDO off
+ */
+void
+r23b_lc_calib(struct rtwn_softc *sc)
+{
+ uint32_t rf_ac[2];
+ uint8_t txmode;
+ int i;
+
+ txmode = rtwn_read_1(sc, R92C_OFDM1_LSTF + 3);
+ if ((txmode & 0x70) != 0) {
+ /* Disable all continuous Tx. */
+ rtwn_write_1(sc, R92C_OFDM1_LSTF + 3, txmode & ~0x70);
+
+ /* Set RF mode to standby mode. */
+ for (i = 0; i < sc->nrxchains; i++) {
+ rf_ac[i] = rtwn_rf_read(sc, i, R92C_RF_AC);
+ rtwn_rf_write(sc, i, R92C_RF_AC,
+ RW(rf_ac[i], R92C_RF_AC_MODE,
+ R92C_RF_AC_MODE_STANDBY));
+ }
+ } else {
+ /* Block all Tx queues. */
+ rtwn_write_1(sc, R92C_TXPAUSE, R92C_TX_QUEUE_ALL);
+ }
+
+ /* LDO on */
+ rtwn_rf_write(sc, 0, R23B_RF_S0S1, 0xdfbe0);
+
+ /* Start calibration. */
+ rtwn_rf_setbits(sc, 0, R92C_RF_CHNLBW, 0, R92C_RF_CHNLBW_LCSTART);
+
+ /* Give calibration the time to complete. */
+ rtwn_delay(sc, 100000); /* 100ms */
+
+ /* LDO off */
+ rtwn_rf_write(sc, 0, R23B_RF_S0S1, 0xdffe0);
+
+ /* Restore configuration. */
+ if ((txmode & 0x70) != 0) {
+ /* Restore Tx mode. */
+ rtwn_write_1(sc, R92C_OFDM1_LSTF + 3, txmode);
+ /* Restore RF mode. */
+ for (i = 0; i < sc->nrxchains; i++)
+ rtwn_rf_write(sc, i, R92C_RF_AC, rf_ac[i]);
+ } else {
+ /* Unblock all Tx queues. */
+ rtwn_write_1(sc, R92C_TXPAUSE, 0x00);
+ }
+}
+
+static void
+r23b_iq_calib_chain(struct rtwn_softc *sc, enum r23b_iqk_stage stage)
+{
+ struct r23b_softc *rs = sc->sc_priv;
+ uint32_t rf_path;
+
+ RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB, "%s: starting stage: %s\n",
+ __func__, stage == R23B_IQK_TX ? "R23B_IQK_TX" :
+ stage == R23B_IQK_TX_RX ? "R23B_IQK_TX_RX" : "R23B_IQK_RX");
+
+ /* Save RF path. */
+ rf_path = rtwn_read_4(sc, R23B_ANT_SEL);
+
+ /* Leave IQK mode. */
+ rtwn_setbits_4(sc, R92C_FPGA0_IQK, ~0xff, 0);
+
+ rtwn_rf_setbits(sc, 0, R88E_RF_WE_LUT, 0, 0x80000);
+ switch (stage) {
+ case R23B_IQK_TX:
+ rtwn_rf_write(sc, 0, R92C_RF_RCK_OS, 0x20000);
+ rtwn_rf_write(sc, 0, R92C_RF_TXPA_G(0), 0x0003f);
+ rtwn_rf_write(sc, 0, R92C_RF_TXPA_G(1), 0xc7f87);
+
+ rtwn_write_4(sc, R92C_TX_IQK, 0x01007c00);
+ break;
+ case R23B_IQK_TX_RX:
+ rtwn_rf_write(sc, 0, R92C_RF_RCK_OS, 0x30000);
+ rtwn_rf_write(sc, 0, R92C_RF_TXPA_G(0), 0x0001f);
+ rtwn_rf_write(sc, 0, R92C_RF_TXPA_G(1), 0xf7fb7);
+
+ rtwn_write_4(sc, R92C_TX_IQK, 0x01007c00);
+ break;
+ case R23B_IQK_RX:
+ rtwn_rf_write(sc, 0, R92C_RF_RCK_OS, 0x30000);
+ rtwn_rf_write(sc, 0, R92C_RF_TXPA_G(0), 0x0001f);
+ rtwn_rf_write(sc, 0, R92C_RF_TXPA_G(1), 0xf7d77);
+
+ rtwn_rf_write(sc, 0, 0xdf, 0xf80);
+ rtwn_rf_write(sc, 0, 0x55, 0x4021f);
+ break;
+ default:
+ KASSERT(0, ("%s: Invalid stage %d", __func__, stage));
+ }
+
+ rtwn_write_4(sc, R92C_RX_IQK, 0x01004800);
+
+ switch (stage) {
+ case R23B_IQK_TX:
+ case R23B_IQK_TX_RX:
+ rtwn_write_4(sc, R92C_TX_IQK_TONE(0), 0x18008c1c);
+ rtwn_write_4(sc, R92C_RX_IQK_TONE(0), 0x38008c1c);
+ break;
+ case R23B_IQK_RX:
+ rtwn_write_4(sc, R92C_TX_IQK_TONE(0), 0x38008c1c);
+ rtwn_write_4(sc, R92C_RX_IQK_TONE(0), 0x18008c1c);
+ break;
+ }
+ rtwn_write_4(sc, R92C_TX_IQK_TONE(1), 0x38008c1c);
+ rtwn_write_4(sc, R92C_RX_IQK_TONE(1), 0x38008c1c);
+
+ switch (stage) {
+ case R23B_IQK_TX:
+ rtwn_write_4(sc, R92C_TX_IQK_PI(0), 0x821403ea);
+ rtwn_write_4(sc, R92C_RX_IQK_PI(0), 0x28110000);
+ break;
+ case R23B_IQK_TX_RX:
+ rtwn_write_4(sc, R92C_TX_IQK_PI(0), 0x82160ff0);
+ rtwn_write_4(sc, R92C_RX_IQK_PI(0), 0x28110000);
+ break;
+ case R23B_IQK_RX:
+ rtwn_write_4(sc, R92C_TX_IQK_PI(0), 0x82110000);
+ rtwn_write_4(sc, R92C_RX_IQK_PI(0), 0x2816001f);
+ break;
+ }
+
+ rtwn_write_4(sc, R92C_TX_IQK_PI(1), 0x82110000);
+ rtwn_write_4(sc, R92C_RX_IQK_PI(1), 0x28110000);
+
+ switch (stage) {
+ case R23B_IQK_TX:
+ rtwn_write_4(sc, R92C_IQK_AGC_RSP, 0x00462911);
+ break;
+ case R23B_IQK_TX_RX:
+ rtwn_write_4(sc, R92C_IQK_AGC_RSP, 0x0046a911);
+ break;
+ case R23B_IQK_RX:
+ rtwn_write_4(sc, R92C_IQK_AGC_RSP, 0x0046a8d1);
+ break;
+ }
+
+ /* Enter IQK mode. */
+ rtwn_setbits_4(sc, R92C_FPGA0_IQK, ~0xff, 0x80800000);
+
+ rtwn_write_4(sc, R23B_ANT_SEL, rs->bt_antnum == 0 ?
+ R23B_ANT_SEL_S0 : R23B_ANT_SEL_S1);
+
+ /* GNT_BT = 0 */
+ rtwn_write_4(sc, R23B_GNT_BT, 0x00000800);
+
+ /* We're doing LO and IQ calibration in one shot. */
+ rtwn_write_4(sc, R92C_IQK_AGC_PTS, 0xf9000000);
+ rtwn_write_4(sc, R92C_IQK_AGC_PTS, 0xf8000000);
+
+ rtwn_delay(sc, 20000); /* 20ms */
+
+ /* Restore RF path. */
+ rtwn_write_4(sc, R23B_ANT_SEL, rf_path);
+
+ /* GNT_BT = 1 */
+ rtwn_write_4(sc, R23B_GNT_BT, 0x00001800);
+
+ /* Leave IQK mode. */
+ rtwn_setbits_4(sc, R92C_FPGA0_IQK, ~0xff, 0);
+}
+
+static bool
+r23b_iq_calib_get_tx_result(struct rtwn_softc *sc,
+ struct r23b_iqk_result *tx_result)
+{
+ uint32_t val;
+
+ val = rtwn_read_4(sc, R92C_RX_POWER_IQK_AFTER(0));
+ RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB, "%s: rx_after (%#x): %#x\n",
+ __func__, R92C_RX_POWER_IQK_AFTER(0), val);
+ if (val & 0x10000000)
+ return (false);
+
+ val = rtwn_read_4(sc, R92C_TX_POWER_IQK_BEFORE(0));
+ RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB, "%s: tx_before (%#x): %#x\n",
+ __func__, R92C_TX_POWER_IQK_BEFORE(0), val);
+ val = MS(val, R92C_POWER_IQK_RESULT);
+ if (val == 0x142 || val >= 0x110 || val <= 0xf0)
+ return (false);
+ tx_result->iqk_before = val;
+
+ val = rtwn_read_4(sc, R92C_TX_POWER_IQK_AFTER(0));
+ RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB, "%s: tx_after (%#x): %#x\n",
+ __func__, R92C_TX_POWER_IQK_AFTER(0), val);
+
+ val = MS(val, R92C_POWER_IQK_RESULT);
+ if (val == 0x42)
+ return (false);
+ tx_result->iqk_after = val;
+
+ if ((val & 0x200) != 0)
+ val = 0x400 - val;
+ return (val < 0xf);
+}
+
+static bool
+r23b_iq_calib_get_rx_result(struct rtwn_softc *sc,
+ struct r23b_iqk_result *rx_result)
+{
+ uint32_t iqk_before, iqk_after, val;
+
+ iqk_after = rtwn_read_4(sc, R92C_RX_POWER_IQK_AFTER(0));
+ RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB, "%s: rx_after (%#x): %#x\n",
+ __func__, R92C_RX_POWER_IQK_AFTER(0), iqk_after);
+
+ iqk_after = MS(iqk_after, R92C_POWER_IQK_RESULT);
+
+ iqk_before = rtwn_read_4(sc, R92C_RX_POWER_IQK_BEFORE(0));
+ RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB, "%s: rx_before (%#x): %#x\n",
+ __func__, R92C_RX_POWER_IQK_BEFORE(0), iqk_before);
+
+ iqk_before = MS(iqk_before, R92C_POWER_IQK_RESULT);
+
+ /* Leave IQK mode. */
+ rtwn_setbits_4(sc, R92C_FPGA0_IQK, ~0xff, 0);
+ rtwn_rf_write(sc, 0, 0xdf, 0x780);
+
+ val = (iqk_after & 0x200) != 0 ? 0x400 - iqk_after : iqk_after;
+ if (iqk_after & 0x8000000)
+ return (false);
+
+ if (iqk_before == 0x132 || iqk_before >= 0x110 || iqk_before <= 0xf0)
+ return (false);
+
+ if (iqk_after == 0x36)
+ return (false);
+
+ if (val >= 0xf)
+ return (false);
+
+ rx_result->iqk_before = iqk_before;
+ rx_result->iqk_after = iqk_after;
+ return (true);
+}
+
+static inline void
+r23b_iq_calib_save_regs(struct rtwn_softc *sc,
+ struct r23b_iqk_reg_vals *vals)
+{
+ for (int i = 0; i < nitems(r23b_iq_calib_reg_adaa); i++)
+ vals->adaa[i] = rtwn_read_4(sc, r23b_iq_calib_reg_adaa[i]);
+
+ vals->txpause = rtwn_read_1(sc, R92C_TXPAUSE);
+
+ for (int i = 0; i < sizeof(vals->bcn_ctrl); i++)
+ vals->bcn_ctrl[i] = rtwn_read_1(sc, R92C_BCN_CTRL(i));
+
+ vals->gpio_muxcfg = rtwn_read_4(sc, R92C_GPIO_MUXCFG);
+
+ for (int i = 0; i < nitems(r23b_iq_calib_reg_iqk_bb); i++)
+ vals->iqk_bb[i] = rtwn_read_4(sc, r23b_iq_calib_reg_iqk_bb[i]);
+}
+
+static inline void
+r23b_iq_calib_restore_regs(struct rtwn_softc *sc,
+ const struct r23b_iqk_reg_vals *vals)
+{
+ for (int i = 0; i < nitems(r23b_iq_calib_reg_adaa); i++)
+ rtwn_write_4(sc, r23b_iq_calib_reg_adaa[i], vals->adaa[i]);
+
+ rtwn_write_1(sc, R92C_TXPAUSE, vals->txpause);
+
+ for (int i = 0; i < sizeof(vals->bcn_ctrl); i++)
+ rtwn_write_1(sc, R92C_BCN_CTRL(i), vals->bcn_ctrl[i]);
+
+ rtwn_write_4(sc, R92C_GPIO_MUXCFG, vals->gpio_muxcfg);
+
+ for (int i = 0; i < nitems(r23b_iq_calib_reg_iqk_bb); i++)
+ rtwn_write_4(sc, r23b_iq_calib_reg_iqk_bb[i], vals->iqk_bb[i]);
+}
+
+static void
+r23b_iq_calib_run(struct rtwn_softc *sc, struct r23b_iqk_result *tx_result,
+ struct r23b_iqk_result *rx_result, const struct r23b_iqk_reg_vals *vals)
+{
+ struct r23b_softc *rs = sc->sc_priv;
+
+ for (int i = 0; i < nitems(r23b_iq_calib_reg_adaa); i++)
+ rtwn_write_4(sc, r23b_iq_calib_reg_adaa[i], 0x01c00014);
+
+ /* MAC settings */
+
+ rtwn_write_1(sc, R92C_TXPAUSE, R92C_TX_QUEUE_AC |
+ R92C_TX_QUEUE_MGT | R92C_TX_QUEUE_HIGH);
+
+ for (int i = 0; i < sizeof(vals->bcn_ctrl); i++) {
+ rtwn_write_1(sc, R92C_BCN_CTRL(i),
+ vals->bcn_ctrl[i] & ~R92C_BCN_CTRL_EN_BCN);
+ }
+
+ rtwn_write_1(sc, R92C_GPIO_MUXCFG,
+ vals->gpio_muxcfg & ~R92C_GPIO_MUXCFG_ENBT);
+
+ /* BB settings */
+
+ rtwn_setbits_4(sc, R92C_CCK0_AFESETTING, 0, 0x0f000000);
+ rtwn_write_4(sc, R92C_OFDM0_TRXPATHENA, 0x03a05600);
+ rtwn_write_4(sc, R92C_OFDM0_TRMUXPAR, 0x000800e4);
+ rtwn_write_4(sc, R92C_FPGA0_RFIFACESW(1), 0x22204000);
+
+ /* IQ calibration settings */
+
+ rtwn_setbits_4(sc, R92C_FPGA0_IQK, ~0xff, 0);
+ rtwn_rf_setbits(sc, 0, R88E_RF_WE_LUT, 0, 0x80000);
+ rtwn_rf_write(sc, 0, R92C_RF_RCK_OS, 0x30000);
+ rtwn_rf_write(sc, 0, R92C_RF_TXPA_G(0), 0x0001f);
+ rtwn_rf_write(sc, 0, R92C_RF_TXPA_G(1), 0xf7fb7);
+ rtwn_rf_setbits(sc, 0, 0xed, 0, 0x20);
+ rtwn_rf_write(sc, 0, 0x43, 0x60fbd);
+
+ /* Path A Tx IQ calibration. */
+ for (int retry = 0; retry < 2; retry++) {
+ r23b_iq_calib_chain(sc, R23B_IQK_TX);
+
+ if (r23b_iq_calib_get_tx_result(sc, tx_result)) {
+ rtwn_setbits_4(sc, R92C_FPGA0_IQK, ~0xff, 0);
+ break;
+ }
+
+ RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB,
+ "%s: Tx failed, retry=%d\n", __func__, retry);
+ }
+
+ /* Path A Rx IQ calibration. */
+ for (int retry = 0; retry < 2; retry++) {
+ struct r23b_iqk_result result;
+
+ r23b_iq_calib_chain(sc, R23B_IQK_TX_RX);
+ if (!r23b_iq_calib_get_tx_result(sc, &result)) {
+ RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB,
+ "%s: Rx (Tx) failed, retry=%d\n", __func__, retry);
+ continue;
+ }
+
+ RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB,
+ "%s: writing %#x to TX_IQK\n", __func__,
+ 0x80007c00 | (result.iqk_before << 16) | result.iqk_after);
+
+ rtwn_write_4(sc, R92C_TX_IQK,
+ 0x80007c00 | (result.iqk_before << 16) | result.iqk_after);
+
+ r23b_iq_calib_chain(sc, R23B_IQK_RX);
+ if (!r23b_iq_calib_get_rx_result(sc, rx_result)) {
+ RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB,
+ "%s: Rx failed, retry=%d\n", __func__, retry);
+ continue;
+ }
+
+ break;
+ }
+
+ if (rs->bt_antnum == 0) {
+ RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB,
+ "%s: path B IQK not implemented!\n", __func__);
+ }
+
+ rtwn_setbits_4(sc, R92C_FPGA0_IQK, ~0xff, 0);
+}
+
+static bool
+r23b_iq_calib_compare_results(
+ struct r23b_iqk_result *tx_result1, struct r23b_iqk_result *rx_result1,
+ struct r23b_iqk_result *tx_result2, struct r23b_iqk_result *rx_result2,
+ struct r23b_iqk_result **tx_candidate,
+ struct r23b_iqk_result **rx_candidate) {
+ uint32_t diff;
+ bool tx_good = false;
+
+ diff = abs(tx_result1->iqk_before - tx_result2->iqk_before);
+ if (diff > R23B_IQK_MAX_TOLERANCE)
+ goto rx_compare;
+
+ diff = abs(R23B_IQK_SIGN(tx_result1->iqk_after) -
+ R23B_IQK_SIGN(tx_result2->iqk_after));
+ if (diff <= R23B_IQK_MAX_TOLERANCE) {
+ *tx_candidate = tx_result1;
+ tx_good = true;
+ }
+
+rx_compare:
+ diff = abs(rx_result1->iqk_before - rx_result2->iqk_before);
+ if (diff > R23B_IQK_MAX_TOLERANCE) {
+ if (tx_good &&
+ rx_result1->iqk_before + rx_result1->iqk_after == 0)
+ *tx_candidate = tx_result2;
+
+ return (false);
+ }
+
+ diff = abs(R23B_IQK_SIGN(rx_result1->iqk_after) -
+ R23B_IQK_SIGN(rx_result2->iqk_after));
+ if (diff > R23B_IQK_MAX_TOLERANCE)
+ return (false);
+
+ *rx_candidate = rx_result1;
+ return (tx_good);
+}
+
+void
+r23b_iq_calib(struct rtwn_softc *sc)
+{
+ struct r23b_iqk_reg_vals vals;
+ struct r23b_iqk_result tx_result[3] = { 0 };
+ struct r23b_iqk_result rx_result[3] = { 0 };
+ struct r23b_iqk_result *tx_candidate = NULL, *rx_candidate = NULL;
+ uint32_t oldval, val;
+ uint32_t gnt_bt;
+
+#ifndef RTWN_WITHOUT_UCODE
+ /* Inform firmware that we're begininng IQK. */
+ r88e_fw_cmd(sc, R23B_CMD_WLAN_CALIB, R23B_CMD_WLAN_CALIB_BEGIN,
+ sizeof(R23B_CMD_WLAN_CALIB_BEGIN));
+#endif
+
+ /* Save GNT_BT. */
+ gnt_bt = rtwn_read_4(sc, R23B_GNT_BT);
+
+ r23b_iq_calib_save_regs(sc, &vals);
+ r23b_iq_calib_run(sc, &tx_result[0], &rx_result[0], &vals);
+ r23b_iq_calib_run(sc, &tx_result[1], &rx_result[1], &vals);
+ r23b_iq_calib_restore_regs(sc, &vals);
+
+ if (r23b_iq_calib_compare_results(&tx_result[0], &rx_result[0],
+ &tx_result[1], &rx_result[1], &tx_candidate, &rx_candidate)) {
+ RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB,
+ "%s: comparing 0 and 1 succeeded\n", __func__);
+ goto write;
+ }
+
+ r23b_iq_calib_run(sc, &tx_result[2], &rx_result[2], &vals);
+ r23b_iq_calib_restore_regs(sc, &vals);
+
+ if (r23b_iq_calib_compare_results(&tx_result[0], &rx_result[0],
+ &tx_result[2], &rx_result[2], &tx_candidate, &rx_candidate)) {
+ RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB,
+ "%s: comparing 0 and 2 succeeded\n", __func__);
+ goto write;
+ }
+
+ if (!r23b_iq_calib_compare_results(&tx_result[1], &rx_result[1],
+ &tx_result[2], &rx_result[2], &tx_candidate, &rx_candidate)) {
+ /* If no candidate was assigned, set the result to zero. */
+ if (tx_candidate == NULL) {
+ tx_result[0] = (struct r23b_iqk_result){ 0 };
+ tx_candidate = &tx_result[0];
+ }
+
+ if (rx_candidate == NULL) {
+ rx_result[0] = (struct r23b_iqk_result){ 0 };
+ rx_candidate = &rx_result[0];
+ }
+
+ if (tx_candidate->iqk_before == 0 &&
+ tx_candidate->iqk_after == 0 &&
+ rx_candidate->iqk_before == 0 &&
+ rx_candidate->iqk_after == 0) {
+ RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB,
+ "%s: IQK failed\n", __func__);
+ goto done;
+ }
+
+ RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB,
+ "%s: comparison failed but a candidate was assigned\n",
+ __func__);
+ } else {
+ RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB,
+ "%s: comparing 1 and 2 succeeded\n", __func__);
+ }
+
+write:
+ RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB, "%s: IQK candidate dump:\n"
+ "\ttx_candidate->iqk_before 0x%x\ttx_candidate->iqk_after 0x%x\n"
+ "\trx_candidate->iqk_before 0x%x\trx_candidate->iqk_after 0x%x\n",
+ __func__, tx_candidate->iqk_before, tx_candidate->iqk_after,
+ rx_candidate->iqk_before, rx_candidate->iqk_after);
+
+ /*
+ * Don't write both the tx and rx configuration
+ * values if tx IQK failed.
+ */
+ if (tx_candidate->iqk_before == 0)
+ goto done;
+
+ /* Write path A values */
+
+ oldval = rtwn_read_4(sc, R92C_OFDM0_TXIQIMBALANCE(0));
+ oldval = (oldval >> 22) & 0x3ff;
+
+ val = (R23B_IQK_SIGN(tx_candidate->iqk_before) * oldval) >> 7;
+ rtwn_setbits_4(sc, R92C_OFDM0_TXIQIMBALANCE(0), 0x3ff,
+ (val >> 1) & 0x3ff);
+ rtwn_setbits_4(sc, R92C_OFDM0_ECCATHRESHOLD, 0x80000000, val << 31);
+
+ val = (R23B_IQK_SIGN(tx_candidate->iqk_after) * oldval) >> 7;
+ rtwn_setbits_4(sc, R92C_TX_POWER_IQK_BEFORE(0),
+ 0xf0000000, (((val >> 1) & 0x3c0) >> 6) << 28);
+ rtwn_setbits_4(sc, R92C_OFDM0_TXIQIMBALANCE(0),
+ 0x3f0000, ((val >> 1) & 0x3f) << 16);
+ rtwn_setbits_4(sc, R92C_OFDM0_ECCATHRESHOLD,
+ 0x10000000, (val << 29) & 0x1);
+
+ /* Skip the rx configuration values if rx IQK failed. */
+ if (rx_candidate->iqk_before == 0)
+ goto done;
+
+ val = rx_candidate->iqk_after & 0x3f;
+ rtwn_setbits_4(sc, R92C_OFDM0_RXIQIMBALANCE(0), 0x3ff,
+ rx_candidate->iqk_before);
+ rtwn_setbits_4(sc, R92C_OFDM0_RXIQIMBALANCE(0), 0xfc00, val << 10);
+
+ val = (rx_candidate->iqk_after >> 6) & 0xf;
+ rtwn_setbits_4(sc, R92C_OFDM0_RXIQEXTANTA, 0xf0000000, val << 28);
+done:
+ /* Restore GNT_BT. */
+ rtwn_write_4(sc, R23B_GNT_BT, gnt_bt);
+
+ rtwn_rf_setbits(sc, 0, R88E_RF_WE_LUT, 0, 0x80000);
+ rtwn_rf_write(sc, 0, R92C_RF_RCK_OS, 0x18000);
+ rtwn_rf_write(sc, 0, R92C_RF_TXPA_G(0), 0x0001f);
+ rtwn_rf_write(sc, 0, R92C_RF_TXPA_G(0), 0xe6177);
+ rtwn_rf_setbits(sc, 0, 0xed, 0, 0x20);
+ rtwn_rf_write(sc, 0, 0x43, 0x300bd);
+
+#ifndef RTWN_WITHOUT_UCODE
+ /* Inform firmware that we're done. */
+ r88e_fw_cmd(sc, R23B_CMD_WLAN_CALIB, R23B_CMD_WLAN_CALIB_END,
+ sizeof(R23B_CMD_WLAN_CALIB_END));
+#endif
+}
+
+void
+r23b_temp_measure(struct rtwn_softc *sc)
+{
+ rtwn_rf_setbits(sc, 0, R88E_RF_T_METER, 0, R88E_RF_T_METER_START);
+}
diff --git a/sys/dev/rtwn/rtl8723b/r23b_fw.c b/sys/dev/rtwn/rtl8723b/r23b_fw.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/rtwn/rtl8723b/r23b_fw.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2026 Ahmad Khalifa <vexeduxr@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/param.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+#include <sys/taskqueue.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/linker.h>
+
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <net/if_media.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_radiotap.h>
+
+#include <dev/rtwn/if_rtwnreg.h>
+#include <dev/rtwn/if_rtwnvar.h>
+
+#include <dev/rtwn/rtl8192c/r92c.h>
+#include <dev/rtwn/rtl8192c/r92c_reg.h>
+
+#include <dev/rtwn/rtl8192e/r92e.h>
+
+#include <dev/rtwn/rtl8723b/r23b.h>
+
+#ifndef RTWN_WITHOUT_UCODE
+void
+r23b_fw_reset(struct rtwn_softc *sc, int reason)
+{
+ switch (reason) {
+ case RTWN_FW_RESET_DOWNLOAD:
+ case RTWN_FW_RESET_SHUTDOWN:
+ return (r92c_fw_reset(sc, reason));
+ case RTWN_FW_RESET_CHECKSUM:
+ return (r92e_fw_reset(sc, reason));
+ }
+}
+
+void
+r23b_fw_download_enable(struct rtwn_softc *sc, int enable)
+{
+ if (!enable) {
+ /* MCU download disable. */
+ rtwn_setbits_1(sc, R92C_MCUFWDL, R92C_MCUFWDL_EN, 0);
+ return;
+ }
+
+ /* 8051 enable. */
+ rtwn_setbits_1_shift(sc, R92C_SYS_FUNC_EN, 0,
+ R92C_SYS_FUNC_EN_CPUEN, 1);
+ /* MCU firmware download enable. */
+ rtwn_setbits_1(sc, R92C_MCUFWDL, 0, R92C_MCUFWDL_EN);
+
+ for (int ntries = 0; ntries <= 100; ntries++) {
+ uint8_t val;
+
+ val = rtwn_read_1(sc, R92C_MCUFWDL);
+ if (val & R92C_MCUFWDL_EN)
+ break;
+
+ rtwn_write_1(sc, R92C_MCUFWDL, val | R92C_MCUFWDL_EN);
+ rtwn_delay(sc, 1000);
+ }
+
+ /* 8051 reset. */
+ rtwn_setbits_1_shift(sc, R92C_MCUFWDL, R92C_MCUFWDL_ROM_DLEN,
+ 0, 2);
+}
+#endif
diff --git a/sys/dev/rtwn/rtl8723b/r23b_fw_cmd.h b/sys/dev/rtwn/rtl8723b/r23b_fw_cmd.h
new file mode 100644
--- /dev/null
+++ b/sys/dev/rtwn/rtl8723b/r23b_fw_cmd.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2026 Ahmad Khalifa <vexeduxr@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef R23B_FW_CMD_H
+#define R23B_FW_CMD_H
+
+/*
+ * Host to firmware commands.
+ */
+#define R23B_CMD_RSSI_SETTING 0x42
+#define R23B_CMD_PS_TDMA 0x60
+#define R23B_CMD_BT_INFO 0x61
+#define R23B_CMD_BT_WLANACT 0x63
+#define R23B_CMD_ANT_INV 0x65
+#define R23B_CMD_BT_MP_OPER 0x67
+#define R23B_CMD_WLAN_CALIB 0x6d
+#define R23B_CMD_BT_GRANT 0x6e
+
+#define R23B_CMD_BT_WLANACT_IGNORE ((uint8_t[]){ 0 })
+
+#define R23B_CMD_WLAN_CALIB_BEGIN ((uint8_t[]){ 1 })
+#define R23B_CMD_WLAN_CALIB_END ((uint8_t[]){ 0 })
+
+#define R23B_CMD_BT_GRANT_LOW ((uint8_t[]){ 0 })
+#define R23B_CMD_BT_GRANT_HIGH ((uint8_t[]){ 1 })
+
+/* Structure for R23B_CMD_RSSI_SETTING. */
+struct r23b_fw_cmd_rssi {
+ uint8_t macid;
+ uint8_t reserved;
+ uint8_t pwdb;
+ uint8_t uldl;
+} __packed;
+
+/* Structure for R23B_CMD_BT_MP_OPER. */
+struct r23b_fw_cmd_btoper {
+ uint8_t req_num;
+ uint8_t opcode;
+#define R23B_BT_MP_OPER_WRITE_VAL 0xd
+#define R23B_BT_MP_OPER_WRITE_ADDR 0xc
+
+ uint8_t value;
+ uint8_t addr;
+} __packed;
+
+/* Structure for R23B_CMD_ANT_INV. */
+struct r23b_fw_cmd_antinv {
+ uint8_t invert;
+ uint8_t internal_switch;
+} __packed;
+
+#endif
diff --git a/sys/dev/rtwn/rtl8723b/r23b_init.c b/sys/dev/rtwn/rtl8723b/r23b_init.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/rtwn/rtl8723b/r23b_init.c
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 2026 Ahmad Khalifa <vexeduxr@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/param.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+#include <sys/taskqueue.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/linker.h>
+
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <net/if_media.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_radiotap.h>
+
+#include <dev/rtwn/if_rtwnreg.h>
+#include <dev/rtwn/if_rtwnvar.h>
+
+#include <dev/rtwn/rtl8188e/r88e.h>
+#include <dev/rtwn/rtl8188e/r88e_reg.h>
+
+#include <dev/rtwn/rtl8192c/r92c_reg.h>
+
+#include <dev/rtwn/rtl8192e/r92e.h>
+#include <dev/rtwn/rtl8192e/r92e_var.h>
+
+#include <dev/rtwn/rtl8723b/r23b.h>
+#include <dev/rtwn/rtl8723b/r23b_reg.h>
+#include <dev/rtwn/rtl8723b/r23b_var.h>
+#include <dev/rtwn/rtl8723b/r23b_fw_cmd.h>
+
+#include <dev/rtwn/rtl8821a/r21a_reg.h>
+
+int
+r23b_set_page_size(struct rtwn_softc *sc)
+{
+ return (rtwn_write_1(sc, R92C_PBP, SM(R92C_PBP_PSRX, R92C_PBP_256) |
+ SM(R92C_PBP_PSTX, R92C_PBP_256)));
+}
+
+void
+r23b_init_antsel(struct rtwn_softc *sc)
+{
+ rtwn_setbits_4(sc, R88E_BB_PAD_CTRL, 0x1100000, 0);
+ rtwn_setbits_4(sc, R92C_GPIO_MUXCFG, 0x10, 0x8);
+ rtwn_setbits_4(sc, R92C_LEDCFG0, 0x400000, 0x800000);
+ rtwn_setbits_4(sc, 0x944, 0, 0x3);
+ rtwn_setbits_4(sc, 0x930, 0xff, 0x77);
+ rtwn_setbits_4(sc, R92C_PWR_DATA, 0, 0x800);
+}
+
+void
+r23b_init_bb_common(struct rtwn_softc *sc)
+{
+ struct r23b_softc *rs = sc->sc_priv;
+ uint8_t val;
+
+ /* PathA RF Power On. */
+ rtwn_write_1(sc, R92C_RF_CTRL,
+ R92C_RF_CTRL_EN | R92C_RF_CTRL_RSTB | R92C_RF_CTRL_SDMRSTB);
+
+ rtwn_delay(sc, 10);
+
+ rtwn_rf_write(sc, 0, R92C_RF_IQADJ_G(0), 0x780);
+
+ rtwn_write_1(sc, R92C_SYS_FUNC_EN, R92C_SYS_FUNC_EN_BBRSTB |
+ R92C_SYS_FUNC_EN_BB_GLB_RST | R92C_SYS_FUNC_EN_DIO_PCIE |
+ R92C_SYS_FUNC_EN_PCIEA | R92C_SYS_FUNC_EN_PPLL);
+
+ rtwn_write_1(sc, R92C_AFE_XTAL_CTRL + 1, 0x80);
+
+ for (int i = 0; i < sc->bb_size; i++) {
+ const struct rtwn_bb_prog *bb_prog = &sc->bb_prog[i];
+
+ for (int j = 0; j < bb_prog->count; j++) {
+ rtwn_write_4(sc, bb_prog->reg[j], bb_prog->val[j]);
+ rtwn_delay(sc, 1);
+ }
+ }
+
+ for (int i = 0; i < sc->agc_size; i++) {
+ const struct rtwn_agc_prog *agc_prog = &sc->agc_prog[i];
+
+ for (int j = 0; j < agc_prog->count; j++) {
+ rtwn_write_4(sc, R92C_OFDM0_AGCRSSITABLE,
+ agc_prog->val[j]);
+ rtwn_delay(sc, 1);
+ }
+ }
+
+ rtwn_write_4(sc, R92C_OFDM0_AGCCORE1(0), 0x69553422);
+ rtwn_delay(sc, 1);
+ rtwn_write_4(sc, R92C_OFDM0_AGCCORE1(0), 0x69553420);
+ rtwn_delay(sc, 1);
+ rtwn_write_4(sc, R92C_HSSI_PARAM2(0), 0x00390204);
+ rtwn_delay(sc, 1);
+
+ val = rs->super.crystalcap & 0x3f;
+ rtwn_setbits_4(sc, R92C_MAC_PHY_CTRL, R21A_MAC_PHY_CRYSTALCAP_M,
+ SM(R21A_MAC_PHY_CRYSTALCAP, val | (val << 6)));
+}
+
+void
+r23b_init_rf(struct rtwn_softc *sc)
+{
+ r92e_init_rf(sc);
+
+ /* Init LCK. */
+ rtwn_rf_write(sc, 0, R23B_RF_S0S1, 0xdfbe0);
+ rtwn_rf_write(sc, 0, R92C_RF_CHNLBW, 0x8c01);
+ rtwn_delay(sc, 200000);
+ rtwn_rf_write(sc, 0, R23B_RF_S0S1, 0xdffe0);
+}
+
+#ifndef RTWN_WITHOUT_UCODE
+static void
+r23b_write_btreg(struct rtwn_softc *sc, uint8_t addr, uint8_t data)
+{
+ struct r23b_fw_cmd_btoper cmd;
+
+ cmd.req_num = 0;
+ cmd.opcode = R23B_BT_MP_OPER_WRITE_VAL;
+ cmd.value = data;
+ cmd.addr = 0;
+ r88e_fw_cmd(sc, R23B_CMD_BT_MP_OPER, &cmd, sizeof(cmd));
+
+ rtwn_delay(sc, 200000); /* 200ms */
+
+ cmd.req_num = 1;
+ cmd.opcode = R23B_BT_MP_OPER_WRITE_ADDR;
+ cmd.value = 0;
+ cmd.addr = addr;
+ r88e_fw_cmd(sc, R23B_CMD_BT_MP_OPER, &cmd, sizeof(cmd));
+}
+#endif
+
+void
+r23b_post_init(struct rtwn_softc *sc)
+{
+#ifndef RTWN_WITHOUT_UCODE
+ struct r23b_fw_cmd_antinv cmd;
+ const uint8_t ps_tdma[5] = { 0x8, 0x0, 0x0, 0x0, 0x0 };
+#endif
+
+ if (sc->macid_rpt2_max_num > 0) {
+ rtwn_setbits_1(sc, R88E_TX_RPT_CTRL, 0, R88E_TX_RPT2_ENA);
+ rtwn_write_1(sc, R88E_TX_RPT_MACID_MAX, sc->macid_rpt2_max_num);
+ /* Enable periodic TX report; 32uS units */
+ rtwn_write_2(sc, R88E_TX_RPT_TIME, 0xcdf0);
+ }
+
+ rtwn_lc_calib(sc);
+ rtwn_iq_calib(sc);
+
+ /* Enable RF */
+
+ /* XXX lots of magic numbers here. */
+
+ rtwn_write_1(sc, 0x790, 0x5);
+ rtwn_write_1(sc, 0x778, 0x1);
+ rtwn_setbits_1(sc, R92C_GPIO_MUXCFG, 0, R92C_GPIO_MUXCFG_ENBT);
+
+ /* WiFi TRx Mask on */
+ rtwn_rf_write(sc, 0, R92C_RF_IQADJ_G(0), 0x780);
+
+#ifndef RTWN_WITHOUT_UCODE
+ /* BT TRx Mask on */
+ r23b_write_btreg(sc, 0x3c, 0x15);
+
+ r88e_fw_cmd(sc, R23B_CMD_BT_GRANT, R23B_CMD_BT_GRANT_LOW,
+ sizeof(R23B_CMD_BT_GRANT_LOW));
+#endif
+
+ rtwn_write_1(sc, 0x76e, 0x4);
+ rtwn_setbits_1(sc, R88E_BB_PAD_CTRL + 3, 0, 0x20);
+ rtwn_setbits_1(sc, R92C_PWR_DATA + 1, 0, 0x8);
+ rtwn_write_1(sc, 0x974, 0xff);
+ rtwn_setbits_1(sc, 0x944, 0, 0x3);
+ rtwn_write_1(sc, 0x930, 0x77);
+
+ rtwn_setbits_4(sc, R92C_LEDCFG0, 0x1000000, 0x800000);
+ rtwn_setbits_1(sc, R88E_BB_PAD_CTRL, 0x1, 0);
+
+#ifndef RTWN_WITHOUT_UCODE
+ /* Inform firmware of the antenna inversion. */
+ cmd.internal_switch = 0;
+ cmd.invert = 1;
+
+ r88e_fw_cmd(sc, R23B_CMD_ANT_INV, &cmd, sizeof(cmd));
+#endif
+
+ rtwn_write_4(sc, R23B_ANT_SEL, 0x80);
+
+#ifndef RTWN_WITHOUT_UCODE
+ r88e_fw_cmd(sc, R23B_CMD_PS_TDMA, ps_tdma, sizeof(ps_tdma));
+#endif
+
+ /* btcoex table */
+ rtwn_write_4(sc, R23B_BTCOEX_TABLE(0), 0x55555555);
+ rtwn_write_4(sc, R23B_BTCOEX_TABLE(1), 0x55555555);
+ rtwn_write_4(sc, R23B_BTCOEX_TABLE(2), 0x00ffffff);
+ rtwn_write_4(sc, R23B_BTCOEX_TABLE(3), 0x00000003);
+
+#ifndef RTWN_WITHOUT_UCODE
+ r88e_fw_cmd(sc, R23B_CMD_BT_INFO, &(uint8_t){ 1 }, sizeof(uint8_t));
+ r88e_fw_cmd(sc, R23B_CMD_BT_WLANACT, R23B_CMD_BT_WLANACT_IGNORE,
+ sizeof(R23B_CMD_BT_WLANACT_IGNORE));
+#endif
+
+ /* ACK for xmit mgmt frames. */
+ rtwn_setbits_4(sc, R92C_FWHW_TXQ_CTRL, 0, R23B_FWHW_TXQ_CTRL_ACK_MGNT);
+
+#ifndef RTWN_WITHOUT_UCODE
+ if (sc->sc_flags & RTWN_FW_LOADED) {
+ if (sc->sc_ratectl_sysctl == RTWN_RATECTL_FW) {
+ /* TODO: implement */
+ sc->sc_ratectl = RTWN_RATECTL_NET80211;
+ } else
+ sc->sc_ratectl = sc->sc_ratectl_sysctl;
+ } else
+#endif
+ sc->sc_ratectl = RTWN_RATECTL_NONE;
+}
diff --git a/sys/dev/rtwn/rtl8723b/r23b_led.c b/sys/dev/rtwn/rtl8723b/r23b_led.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/rtwn/rtl8723b/r23b_led.c
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2026 Ahmad Khalifa <vexeduxr@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/param.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+#include <sys/taskqueue.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/linker.h>
+
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <net/if_media.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_radiotap.h>
+
+#include <dev/rtwn/if_rtwnreg.h>
+#include <dev/rtwn/if_rtwnvar.h>
+
+#include <dev/rtwn/rtl8192c/r92c_reg.h>
+
+#include <dev/rtwn/rtl8723b/r23b.h>
+#include <dev/rtwn/rtl8723b/r23b_reg.h>
+
+void
+r23b_set_led(struct rtwn_softc *sc, int led, int on)
+{
+ if (led != RTWN_LED_LINK)
+ return;
+
+ sc->ledlink = on;
+ rtwn_setbits_1(sc, R92C_LEDCFG2, R23B_LEDCFG2_LED_M,
+ R23B_LEDCFG2_LED_SW_CTRL | (on ? 0 : R23B_LEDCFG2_LED_DISABLE));
+}
diff --git a/sys/dev/rtwn/rtl8723b/r23b_priv.h b/sys/dev/rtwn/rtl8723b/r23b_priv.h
new file mode 100644
--- /dev/null
+++ b/sys/dev/rtwn/rtl8723b/r23b_priv.h
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2026 Ahmad Khalifa <vexeduxr@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef R23B_PRIV_H
+#define R23B_PRIV_H
+
+#include <dev/rtwn/rtl8723b/r23b_rom_defs.h>
+
+static const struct rtwn_mac_prog rtl8723b_mac[] = {
+ { 0x02f, 0x30 }, { 0x035, 0x00 }, { 0x039, 0x08 }, { 0x04e, 0xe0 },
+ { 0x064, 0x00 }, { 0x067, 0x20 }, { 0x428, 0x0a }, { 0x429, 0x10 },
+ { 0x430, 0x00 }, { 0x431, 0x00 }, { 0x432, 0x00 }, { 0x433, 0x01 },
+ { 0x434, 0x04 }, { 0x435, 0x05 }, { 0x436, 0x07 }, { 0x437, 0x08 },
+ { 0x43c, 0x04 }, { 0x43d, 0x05 }, { 0x43e, 0x07 }, { 0x43f, 0x08 },
+ { 0x440, 0x5d }, { 0x441, 0x01 }, { 0x442, 0x00 }, { 0x444, 0x10 },
+ { 0x445, 0x00 }, { 0x446, 0x00 }, { 0x447, 0x00 }, { 0x448, 0x00 },
+ { 0x449, 0xf0 }, { 0x44a, 0x0f }, { 0x44b, 0x3e }, { 0x44c, 0x10 },
+ { 0x44d, 0x00 }, { 0x44e, 0x00 }, { 0x44f, 0x00 }, { 0x450, 0x00 },
+ { 0x451, 0xf0 }, { 0x452, 0x0f }, { 0x453, 0x00 }, { 0x456, 0x5e },
+ { 0x460, 0x66 }, { 0x461, 0x66 }, { 0x4c8, 0xff }, { 0x4c9, 0x08 },
+ { 0x4cc, 0xff }, { 0x4cd, 0xff }, { 0x4ce, 0x01 }, { 0x500, 0x26 },
+ { 0x501, 0xa2 }, { 0x502, 0x2f }, { 0x503, 0x00 }, { 0x504, 0x28 },
+ { 0x505, 0xa3 }, { 0x506, 0x5e }, { 0x507, 0x00 }, { 0x508, 0x2b },
+ { 0x509, 0xa4 }, { 0x50a, 0x5e }, { 0x50b, 0x00 }, { 0x50c, 0x4f },
+ { 0x50d, 0xa4 }, { 0x50e, 0x00 }, { 0x50f, 0x00 }, { 0x512, 0x1c },
+ { 0x514, 0x0a }, { 0x516, 0x0a }, { 0x525, 0x4f }, { 0x550, 0x10 },
+ { 0x551, 0x10 }, { 0x559, 0x02 }, { 0x55c, 0x50 }, { 0x55d, 0xff },
+ { 0x605, 0x30 }, { 0x608, 0x0e }, { 0x609, 0x2a }, { 0x620, 0xff },
+ { 0x621, 0xff }, { 0x622, 0xff }, { 0x623, 0xff }, { 0x624, 0xff },
+ { 0x625, 0xff }, { 0x626, 0xff }, { 0x627, 0xff }, { 0x638, 0x50 },
+ { 0x63c, 0x0a }, { 0x63d, 0x0a }, { 0x63e, 0x0e }, { 0x63f, 0x0e },
+ { 0x640, 0x40 }, { 0x642, 0x40 }, { 0x643, 0x00 }, { 0x652, 0xc8 },
+ { 0x66e, 0x05 }, { 0x700, 0x21 }, { 0x701, 0x43 }, { 0x702, 0x65 },
+ { 0x703, 0x87 }, { 0x708, 0x21 }, { 0x709, 0x43 }, { 0x70a, 0x65 },
+ { 0x70b, 0x87 }, { 0x765, 0x18 }, { 0x76e, 0x04 }
+};
+
+static const uint16_t rtl8723b_bb_regs[] = {
+ 0x800, 0x804, 0x808, 0x80c, 0x810, 0x814, 0x818, 0x81c, 0x820, 0x824,
+ 0x828, 0x82c, 0x830, 0x834, 0x838, 0x83c, 0x840, 0x844, 0x848, 0x84c,
+ 0x850, 0x854, 0x858, 0x85c, 0x860, 0x864, 0x868, 0x86c, 0x870, 0x874,
+ 0x878, 0x87c, 0x880, 0x884, 0x888, 0x88c, 0x890, 0x894, 0x898, 0x89c,
+ 0x900, 0x904, 0x908, 0x90c, 0x910, 0x914, 0xa00, 0xa04, 0xa08, 0xa0c,
+ 0xa10, 0xa14, 0xa18, 0xa1c, 0xa20, 0xa24, 0xa28, 0xa2c, 0xa70, 0xa74,
+ 0xa78, 0xa7c, 0xa80, 0xb2c, 0xc00, 0xc04, 0xc08, 0xc0c, 0xc10, 0xc14,
+ 0xc18, 0xc1c, 0xc20, 0xc24, 0xc28, 0xc2c, 0xc30, 0xc34, 0xc38, 0xc3c,
+ 0xc40, 0xc44, 0xc48, 0xc4c, 0xc50, 0xc54, 0xc58, 0xc5c, 0xc60, 0xc64,
+ 0xc68, 0xc6c, 0xc70, 0xc74, 0xc78, 0xc7c, 0xc80, 0xc84, 0xc88, 0xc8c,
+ 0xc90, 0xc94, 0xc98, 0xc9c, 0xca0, 0xca4, 0xca8, 0xcac, 0xcb0, 0xcb4,
+ 0xcb8, 0xcbc, 0xcc0, 0xcc4, 0xcc8, 0xccc, 0xcd0, 0xcd4, 0xcd8, 0xcdc,
+ 0xce0, 0xce4, 0xce8, 0xcec, 0xd00, 0xd04, 0xd08, 0xd0c, 0xd10, 0xd14,
+ 0xd18, 0xd2c, 0xd30, 0xd34, 0xd38, 0xd3c, 0xd40, 0xd44, 0xd48, 0xd4c,
+ 0xd50, 0xd54, 0xd58, 0xd5c, 0xd60, 0xd64, 0xd68, 0xd6c, 0xd70, 0xd74,
+ 0xd78, 0xe00, 0xe04, 0xe08, 0xe10, 0xe14, 0xe18, 0xe1c, 0xe28, 0xe30,
+ 0xe34, 0xe38, 0xe3c, 0xe40, 0xe44, 0xe48, 0xe4c, 0xe50, 0xe54, 0xe58,
+ 0xe5c, 0xe60, 0xe68, 0xe6c, 0xe70, 0xe74, 0xe78, 0xe7c, 0xe80, 0xe84,
+ 0xe88, 0xe8c, 0xed0, 0xed4, 0xed8, 0xedc, 0xee0, 0xeec, 0xf14, 0xf4c,
+ 0xf00, 0x820, 0x800
+};
+
+static const uint32_t rtl8723b_bb_vals[] = {
+ 0x80040000, 0x00000003, 0x0000fc00, 0x0000000a, 0x10001331, 0x020c3d10,
+ 0x02200385, 0x00000000, 0x01000100, 0x00190204, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00010000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x569a11a9, 0x01000014,
+ 0x66f60110, 0x061f0649, 0x00000000, 0x27272700, 0x07000760, 0x25004000,
+ 0x00000808, 0x00000000, 0xb0000c1c, 0x00000001, 0x00000000, 0xccc000c0,
+ 0x00000800, 0xfffffffe, 0x40302010, 0x00706050, 0x00000000, 0x00000023,
+ 0x00000000, 0x81121111, 0x00000002, 0x00000201, 0x00d047c8, 0x80ff800c,
+ 0x8c838300, 0x2e7f120f, 0x9500bb78, 0x1114d028, 0x00881117, 0x89140f00,
+ 0x1a1b0000, 0x090e1317, 0x00000204, 0x00d30000, 0x101fbf00, 0x00000007,
+ 0x00000900, 0x225b0606, 0x21806490, 0x00000000, 0x48071d40, 0x03a05611,
+ 0x000000e4, 0x6c6c6c6c, 0x08800000, 0x40000100, 0x08800000, 0x40000100,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x69e9ac44, 0x469652af,
+ 0x49795994, 0x0a97971c, 0x1f7c403f, 0x000100b7, 0xec020107, 0x007f037f,
+ 0x69553420, 0x43bc0094, 0x00013149, 0x00250492, 0x00000000, 0x7112848b,
+ 0x47c00bff, 0x00000036, 0x2c7f000d, 0x020610db, 0x0000001f, 0x00b91612,
+ 0x390000e4, 0x20f60000, 0x40000100, 0x20200000, 0x00020e1a, 0x00000000,
+ 0x00020e1a, 0x00007f7f, 0x00000000, 0x000300a0, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x28000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x64b22427, 0x00766932,
+ 0x00222222, 0x00000000, 0x37644302, 0x2f97d40c, 0x00000740, 0x40020401,
+ 0x0000907f, 0x20010201, 0xa0633333, 0x3333bc53, 0x7a8f5b6f, 0xcc979975,
+ 0x00000000, 0x80608000, 0x00000000, 0x00127353, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x6437140a, 0x00000000, 0x00000282, 0x30032064,
+ 0x4653de68, 0x04518a3c, 0x00002101, 0x2a201c16, 0x1812362e, 0x322c2220,
+ 0x000e3c24, 0x2d2d2d2d, 0x2d2d2d2d, 0x0390272d, 0x2d2d2d2d, 0x2d2d2d2d,
+ 0x2d2d2d2d, 0x2d2d2d2d, 0x00000000, 0x1000dc1f, 0x10008c1f, 0x02140102,
+ 0x681604c2, 0x01007c00, 0x01004800, 0xfb000000, 0x000028d1, 0x1000dc1f,
+ 0x10008c1f, 0x02140102, 0x28160d05, 0x00000008, 0x001b2556, 0x00c00096,
+ 0x00c00096, 0x01000056, 0x01000014, 0x01000056, 0x01000014, 0x00c00096,
+ 0x01000056, 0x00c00096, 0x00c00096, 0x00c00096, 0x00c00096, 0x000000d6,
+ 0x000000d6, 0x01c00016, 0x00000003, 0x00000000, 0x00000300, 0x01000100,
+ 0x83040000
+};
+
+static const struct rtwn_bb_prog rtl8723b_bb[] = {
+ {
+ nitems(rtl8723b_bb_regs),
+ rtl8723b_bb_regs,
+ rtl8723b_bb_vals,
+ { 0 },
+ NULL
+ }
+};
+
+static const uint32_t rtl8723b_agc_vals[] = {
+ 0xfd000001, 0xfc010001, 0xfb020001, 0xfa030001, 0xf9040001, 0xf8050001,
+ 0xf7060001, 0xf6070001, 0xf5080001, 0xf4090001, 0xf30a0001, 0xf20b0001,
+ 0xf10c0001, 0xf00d0001, 0xef0e0001, 0xee0f0001, 0xed100001, 0xec110001,
+ 0xeb120001, 0xea130001, 0xe9140001, 0xe8150001, 0xe7160001, 0xe6170001,
+ 0xe5180001, 0xe4190001, 0xe31a0001, 0xa51b0001, 0xa41c0001, 0xa31d0001,
+ 0x671e0001, 0x661f0001, 0x65200001, 0x64210001, 0x63220001, 0x4a230001,
+ 0x49240001, 0x48250001, 0x47260001, 0x46270001, 0x45280001, 0x44290001,
+ 0x432a0001, 0x422b0001, 0x292c0001, 0x282d0001, 0x272e0001, 0x262f0001,
+ 0x0a300001, 0x09310001, 0x08320001, 0x07330001, 0x06340001, 0x05350001,
+ 0x04360001, 0x03370001, 0x02380001, 0x01390001, 0x013a0001, 0x013b0001,
+ 0x013c0001, 0x013d0001, 0x013e0001, 0x013f0001, 0xfc400001, 0xfb410001,
+ 0xfa420001, 0xf9430001, 0xf8440001, 0xf7450001, 0xf6460001, 0xf5470001,
+ 0xf4480001, 0xf3490001, 0xf24a0001, 0xf14b0001, 0xf04c0001, 0xef4d0001,
+ 0xee4e0001, 0xed4f0001, 0xec500001, 0xeb510001, 0xea520001, 0xe9530001,
+ 0xe8540001, 0xe7550001, 0xe6560001, 0xe5570001, 0xe4580001, 0xe3590001,
+ 0xa65a0001, 0xa55b0001, 0xa45c0001, 0xa35d0001, 0x675e0001, 0x665f0001,
+ 0x65600001, 0x64610001, 0x63620001, 0x62630001, 0x61640001, 0x48650001,
+ 0x47660001, 0x46670001, 0x45680001, 0x44690001, 0x436a0001, 0x426b0001,
+ 0x286c0001, 0x276d0001, 0x266e0001, 0x256f0001, 0x24700001, 0x09710001,
+ 0x08720001, 0x07730001, 0x06740001, 0x05750001, 0x04760001, 0x03770001,
+ 0x02780001, 0x01790001, 0x017a0001, 0x017b0001, 0x017c0001, 0x017d0001,
+ 0x017e0001, 0x017f0001
+};
+
+static const struct rtwn_agc_prog rtl8723b_agc[] = {
+ {
+ nitems(rtl8723b_agc_vals),
+ rtl8723b_agc_vals,
+ { 0 },
+ NULL
+ }
+};
+
+static const uint8_t rtl8723b_rf_regs[] = {
+ 0x000, 0x0b0, 0x0fe, 0x0fe, 0x0fe, 0x0b1, 0x0fe, 0x0fe, 0x0fe, 0x0b2,
+ 0x0b5, 0x0b6, 0x0b7, 0x0b8, 0x05c, 0x07c, 0x07e, 0x08b, 0x0b0, 0x01c,
+ 0x01e, 0x0df, 0x050,
+
+ /*
+ * The vendor driver has a condition here that sets this to 0x0006b10e
+ * if the package type is TFBGA90, TFBGA80 or TFBGA79.
+ *
+ * However, it seems to never actually read the package type from the
+ * ROM, so in practice this is always ignored.
+ */
+ 0x051,
+
+ 0x052, 0x053, 0x054, 0x055, 0x0dd, 0x070,
+
+ /* Same here. */
+ 0x071,
+
+ 0x072, 0x073, 0x074, 0x075, 0x0ef, 0x034, 0x035, 0x034, 0x035, 0x034,
+ 0x035, 0x034, 0x035, 0x034, 0x035, 0x034, 0x035, 0x034, 0x035, 0x034,
+ 0x035, 0x034, 0x035, 0x034, 0x035, 0x034, 0x035, 0x0ef, 0x0ef, 0x0ed,
+ 0x044, 0x044, 0x044, 0x044, 0x044, 0x044, 0x044, 0x044, 0x044, 0x044,
+ 0x044, 0x0ef, 0x0ed, 0x07f, 0x0ef, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b,
+ 0x03b, 0x03b, 0x03b, 0x0ef, 0x0ed, 0x040, 0x040, 0x040, 0x040, 0x040,
+ 0x040, 0x040, 0x040, 0x0ed, 0x082, 0x083, 0x084, 0x085, 0x0a2, 0x0a3,
+ 0x0a4, 0x0a5, 0x0ed, 0x0ef, 0x056, 0x076, 0x001
+};
+
+static const uint32_t rtl8723b_rf_vals[] = {
+ 0x00010000, 0x000dffe0, 0x00000000, 0x00000000, 0x00000000, 0x00000018,
+ 0x00000000, 0x00000000, 0x00000000, 0x00084c00, 0x0000d2cc, 0x000925aa,
+ 0x00000010, 0x0000907f, 0x00000002, 0x00000002, 0x00000005, 0x0006fc00,
+ 0x000ff9f0, 0x000739d2, 0x00000000, 0x00000780, 0x00067435,
+
+ 0x0006b04e,
+
+ 0x000007D2, 0x00000000, 0x00050400, 0x0004026E, 0x0000004C, 0x00067435,
+
+ 0x0006b04e,
+
+ 0x000007d2, 0x00000000, 0x00050400, 0x0004026e, 0x00000100, 0x0000add7,
+ 0x00005c00, 0x00009dd4, 0x00005000, 0x00008dd1, 0x00004400, 0x00007dce,
+ 0x00003800, 0x00006cd1, 0x00004400, 0x00005cce, 0x00003800, 0x000048ce,
+ 0x00004400, 0x000034ce, 0x00003800, 0x00002451, 0x00004400, 0x0000144e,
+ 0x00003800, 0x00000051, 0x00004400, 0x00000000, 0x00000100, 0x00000010,
+ 0x0000add7, 0x00009dd4, 0x00008dd1, 0x00007dce, 0x00006cc1, 0x00005cce,
+ 0x000044d1, 0x000034ce, 0x00002451, 0x0000144e, 0x00000051, 0x00000000,
+ 0x00000000, 0x00020080, 0x00002000, 0x000380ef, 0x000302fe, 0x00028ce6,
+ 0x000200bc, 0x000188a5, 0x00010fbc, 0x00008f71, 0x00000900, 0x00000000,
+ 0x00000001, 0x000380ef, 0x000302fe, 0x00028ce6, 0x000200bc, 0x000188a5,
+ 0x00010fbc, 0x00008f71, 0x00000900, 0x00000000, 0x00080000, 0x00008000,
+ 0x00048d80, 0x00068000, 0x00080000, 0x00008000, 0x00048d80, 0x00068000,
+ 0x00000002, 0x00000002, 0x00000032, 0x00000032, 0x00000780
+};
+
+static const struct rtwn_rf_prog rtl8723b_rf[] = {
+ {
+ nitems(rtl8723b_rf_regs),
+ rtl8723b_rf_regs,
+ rtl8723b_rf_vals,
+ { 0 },
+ NULL
+ },
+ { 0, NULL, NULL, { 0 }, NULL }
+};
+
+#endif
diff --git a/sys/dev/rtwn/rtl8723b/r23b_reg.h b/sys/dev/rtwn/rtl8723b/r23b_reg.h
new file mode 100644
--- /dev/null
+++ b/sys/dev/rtwn/rtl8723b/r23b_reg.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2026 Ahmad Khalifa <vexeduxr@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef R23B_REG_H
+#define R23B_REG_H
+
+#define R23B_BTCOEX_TABLE(i) (0x6c0 + (i) * 4)
+#define R23B_GNT_BT 0x764
+#define R23B_ANT_SEL 0x948
+
+/* Bits for R92C_EFUSE_TEST. */
+#define R23B_EFUSE_TEST_SEL_M 0x300
+#define R23B_EFUSE_TEST_SEL_S 8
+#define R23B_EFUSE_TEST_SEL_WIFI 0
+#define R23B_EFUSE_TEST_SEL_BT0 1
+#define R23B_EFUSE_TEST_SEL_BT1 2
+#define R23B_EFUSE_TEST_SEL_BT2 3
+#define R23B_EFUSE_TEST_PGMEN 0x800
+
+/* Bits for R92C_PWR_DATA. */
+#define R23B_PWR_DATA_RFE_CTRL_EN 0x800
+
+/* Bits for R92C_LEDCFG0. */
+#define R23B_LEDCFG0_DPDT_SEL_EN 0x800000
+
+/* Bits for R92C_LEDCFG2. */
+#define R23B_LEDCFG2_LED_SW_CTRL 0x20
+#define R23B_LEDCFG2_LED_DISABLE 0x04
+#define R23B_LEDCFG2_LED_M 0x7f
+
+/* Bits for R88E_BB_PAD_CTRL. */
+#define R23B_BB_PAD_CTRL_DPDT_SEL_DATA 0x1
+
+/* Bits for R92C_RXDMA_AGG_PG_TH. */
+#define R23B_RXDMA_AGG_PG_TH_EN 0x80000000
+#define R23B_RXDMA_AGG_PG_TH_SIZE_M 0xf
+#define R23B_RXDMA_AGG_PG_TH_SIZE_S 0
+#define R23B_RXDMA_AGG_PG_TH_TIME_M 0xff00
+#define R23B_RXDMA_AGG_PG_TH_TIME_S 8
+
+/* Bits for R92C_FWHW_TXQ_CTRL. */
+#define R23B_FWHW_TXQ_CTRL_ACK_MGNT 0x1000
+
+/* Bits for R92C_WMAC_TRXPTCL_CTL. */
+#define R23B_WMAC_TRXPTCL_CTL_HT40 0x80
+#define R23B_WMAC_TRXPTCL_CTL_VHT80 0x100
+
+/* Bits for R23B_ANT_SEL. */
+#define R23B_ANT_SEL_S0 0x000
+#define R23B_ANT_SEL_S1 0x280
+
+/*
+ * RF (6052) registers.
+ */
+#define R23B_RF_S0S1 0xb0
+
+/* Bits for R92C_RF_CHNLBW. */
+#define R23B_RF_CHNLBW_BW40 0x00400
+
+#endif
diff --git a/sys/dev/rtwn/rtl8723b/r23b_rf.c b/sys/dev/rtwn/rtl8723b/r23b_rf.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/rtwn/rtl8723b/r23b_rf.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2026 Ahmad Khalifa <vexeduxr@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/param.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+#include <sys/taskqueue.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/linker.h>
+
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <net/if_media.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_radiotap.h>
+
+#include <dev/rtwn/if_rtwnreg.h>
+#include <dev/rtwn/if_rtwnvar.h>
+
+#include <dev/rtwn/rtl8188e/r88e_reg.h>
+
+#include <dev/rtwn/rtl8192c/r92c_reg.h>
+
+#include <dev/rtwn/rtl8723b/r23b.h>
+
+void
+r23b_rf_write(struct rtwn_softc *sc, int chain, uint8_t addr, uint32_t val)
+{
+ uint32_t reg;
+
+ rtwn_write_4(sc, R92C_LSSI_PARAM(chain),
+ SM(R88E_LSSI_PARAM_ADDR, addr) | SM(R92C_LSSI_PARAM_DATA, val));
+
+ /*
+ * The vendor driver claims that writing to 0xb6 may fail under high
+ * temperature conditions, but only attempts to retry during RF init.
+ *
+ * It is not mentioned why 0xb2 is also special.
+ */
+ switch (addr) {
+ case 0xb6:
+ rtwn_delay(sc, 1);
+
+ reg = rtwn_rf_read(sc, chain, addr);
+ rtwn_delay(sc, 1);
+
+ for (int retry = 0; (reg & ~0xff) != (val & ~0xff) &&
+ retry < 6; retry++) {
+ rtwn_write_4(sc, R92C_LSSI_PARAM(chain),
+ SM(R88E_LSSI_PARAM_ADDR, addr) |
+ SM(R92C_LSSI_PARAM_DATA, val));
+ rtwn_delay(sc, 1);
+
+ reg = rtwn_rf_read(sc, chain, addr);
+ }
+ break;
+ case 0xb2:
+ rtwn_delay(sc, 1);
+
+ reg = rtwn_rf_read(sc, chain, addr);
+ rtwn_delay(sc, 1);
+
+ for (int retry = 0; reg != val && retry < 6; retry++) {
+ rtwn_write_4(sc, R92C_LSSI_PARAM(chain),
+ SM(R88E_LSSI_PARAM_ADDR, addr) |
+ SM(R92C_LSSI_PARAM_DATA, val));
+ rtwn_delay(sc, 1);
+
+ rtwn_rf_write(sc, chain, R92C_RF_CHNLBW, 0x0fc07);
+ rtwn_delay(sc, 1);
+
+ reg = rtwn_rf_read(sc, chain, addr);
+ }
+ break;
+ }
+}
diff --git a/sys/dev/rtwn/rtl8723b/r23b_rom.c b/sys/dev/rtwn/rtl8723b/r23b_rom.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/rtwn/rtl8723b/r23b_rom.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2026 Ahmad Khalifa <vexeduxr@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/param.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+#include <sys/taskqueue.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/linker.h>
+
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <net/if_media.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_radiotap.h>
+
+#include <dev/rtwn/if_rtwnreg.h>
+#include <dev/rtwn/if_rtwnvar.h>
+
+#include <dev/rtwn/rtl8192c/r92c_reg.h>
+#include <dev/rtwn/rtl8192c/r92c_rom_defs.h>
+
+#include <dev/rtwn/rtl8192e/r92e_var.h>
+
+#include <dev/rtwn/rtl8723b/r23b.h>
+#include <dev/rtwn/rtl8723b/r23b_reg.h>
+#include <dev/rtwn/rtl8723b/r23b_var.h>
+#include <dev/rtwn/rtl8723b/r23b_rom_image.h>
+
+#include <dev/rtwn/rtl8812a/r12a_rom_image.h>
+
+int
+r23b_efuse_preread(struct rtwn_softc *sc)
+{
+ int error;
+
+ /* Switch to the WIFI bank. */
+ error = rtwn_setbits_4(sc, R92C_EFUSE_TEST, R23B_EFUSE_TEST_SEL_M,
+ SM(R23B_EFUSE_TEST_SEL, R23B_EFUSE_TEST_SEL_WIFI));
+ if (error != 0)
+ return (error);
+
+ /* NB: Vendor driver does this on every read. */
+ error = rtwn_setbits_2(sc, R92C_EFUSE_TEST, R23B_EFUSE_TEST_PGMEN, 0);
+ return (error);
+}
+
+void
+r23b_parse_rom(struct rtwn_softc *sc, uint8_t *buf)
+{
+ struct r23b_rom *rom = (struct r23b_rom *)buf;
+ struct r23b_softc *rs = sc->sc_priv;
+
+ for (int i = 0; i < sc->ntxchains; i++) {
+ struct r23b_tx_pwr_2g *pwr_2g = &rom->tx_pwr[i].pwr_2g;
+ struct r23b_tx_pwr_diff_2g *pwr_diff_2g =
+ &rom->tx_pwr[i].pwr_diff_2g;
+ int j;
+
+ for (j = 0; j < R23B_GROUP_2G - 1; j++) {
+ rs->super.cck_tx_pwr[i][j] = RTWN_GET_ROM_VAR(
+ pwr_2g->cck[j], R23B_DEF_TX_PWR_2G);
+ rs->super.ht40_tx_pwr_2g[i][j] =
+ RTWN_GET_ROM_VAR(pwr_2g->ht40[j],
+ R23B_DEF_TX_PWR_2G);
+ }
+ rs->super.cck_tx_pwr[i][j] = RTWN_GET_ROM_VAR(pwr_2g->cck[j],
+ R23B_DEF_TX_PWR_2G);
+
+ rs->super.cck_tx_pwr_diff_2g[i][0] = 0;
+ rs->super.bw40_tx_pwr_diff_2g[i][0] = 0;
+ if (pwr_diff_2g->ht20_ofdm == 0xff) {
+ rs->super.ofdm_tx_pwr_diff_2g[i][0] =
+ R23B_DEF_TX_OFDM_DIFF;
+ rs->super.bw20_tx_pwr_diff_2g[i][0] =
+ R23B_DEF_TX_HT20_DIFF;
+ } else {
+ rs->super.ofdm_tx_pwr_diff_2g[i][0] = RTWN_SIGN4TO8(
+ MS(pwr_diff_2g->ht20_ofdm, LOW_PART));
+ rs->super.bw20_tx_pwr_diff_2g[i][0] = RTWN_SIGN4TO8(
+ MS(pwr_diff_2g->ht20_ofdm, HIGH_PART));
+ }
+
+ for (j = 1; j < R23B_MAX_TX_COUNT; j++) {
+ struct r23b_tx_pwr_diff123_2g *diff =
+ &pwr_diff_2g->diff123[j - 1];
+
+ if (diff->ofdm_cck == 0xff) {
+ rs->super.ofdm_tx_pwr_diff_2g[i][j] =
+ rs->super.cck_tx_pwr_diff_2g[i][j] =
+ R23B_DEF_TX_DIFF;
+ } else {
+ rs->super.cck_tx_pwr_diff_2g[i][j] =
+ RTWN_SIGN4TO8(MS(diff->ofdm_cck, LOW_PART));
+ rs->super.ofdm_tx_pwr_diff_2g[i][j] =
+ RTWN_SIGN4TO8(
+ MS(diff->ofdm_cck, HIGH_PART));
+ }
+
+ if (diff->ht40_ht20 == 0xff) {
+ rs->super.bw40_tx_pwr_diff_2g[i][j] =
+ rs->super.bw20_tx_pwr_diff_2g[i][j] =
+ R23B_DEF_TX_DIFF;
+ } else {
+ rs->super.bw40_tx_pwr_diff_2g[i][j] =
+ RTWN_SIGN4TO8(
+ MS(diff->ht40_ht20, HIGH_PART));
+ rs->super.bw20_tx_pwr_diff_2g[i][j] =
+ RTWN_SIGN4TO8(
+ MS(diff->ht40_ht20, LOW_PART));
+ }
+ }
+
+ rs->bt_antnum = rom->rf_bt_opt & R12A_RF_BT_OPT_ANT_NUM;
+ }
+
+ rs->super.crystalcap = RTWN_GET_ROM_VAR(rom->crystal_cap,
+ R23B_ROM_CRYSTALCAP_DEF);
+
+ sc->thermal_meter = RTWN_GET_ROM_VAR(rom->thermal_meter,
+ R23B_ROM_THERMALMETER_DEF);
+}
diff --git a/sys/dev/rtwn/rtl8723b/r23b_rom_defs.h b/sys/dev/rtwn/rtl8723b/r23b_rom_defs.h
new file mode 100644
--- /dev/null
+++ b/sys/dev/rtwn/rtl8723b/r23b_rom_defs.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2026 Ahmad Khalifa <vexeduxr@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef R23B_ROM_DEFS_H
+#define R23B_ROM_DEFS_H
+
+#define R23B_MAX_TX_COUNT 4
+#define R23B_MAX_RF_PATH 4
+#define R23B_GROUP_2G 6
+
+#define R23B_EFUSE_MAX_LEN 512
+#define R23B_EFUSE_MAP_LEN 512
+
+#endif /* R23B_ROM_DEFS_H */
diff --git a/sys/dev/rtwn/rtl8723b/r23b_rom_image.h b/sys/dev/rtwn/rtl8723b/r23b_rom_image.h
new file mode 100644
--- /dev/null
+++ b/sys/dev/rtwn/rtl8723b/r23b_rom_image.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2026 Ahmad Khalifa <vexeduxr@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef R23B_ROM_IMAGE_H
+#define R23B_ROM_IMAGE_H
+
+#include <dev/rtwn/rtl8723b/r23b_rom_defs.h>
+
+#define R23B_DEF_TX_PWR_2G 0x2d
+#define R23B_DEF_TX_HT20_DIFF 0x02
+#define R23B_DEF_TX_OFDM_DIFF 0x04
+#define R23B_DEF_TX_DIFF 0xfe
+
+struct r23b_tx_pwr_2g {
+ uint8_t cck[R23B_GROUP_2G];
+ uint8_t ht40[R23B_GROUP_2G - 1];
+} __packed;
+
+struct r23b_tx_pwr_diff123_2g {
+ uint8_t ht40_ht20;
+ uint8_t ofdm_cck;
+} __packed;
+
+struct r23b_tx_pwr_diff_2g {
+ uint8_t ht20_ofdm;
+ struct r23b_tx_pwr_diff123_2g diff123[R23B_MAX_TX_COUNT - 1];
+} __packed;
+
+struct r23b_tx_pwr {
+ struct r23b_tx_pwr_2g pwr_2g;
+ struct r23b_tx_pwr_diff_2g pwr_diff_2g;
+ uint8_t reserved[24];
+} __packed;
+
+struct r23b_rom {
+ uint16_t id;
+ uint8_t reserved1[14];
+ struct r23b_tx_pwr tx_pwr[R23B_MAX_RF_PATH];
+ uint8_t channel_plan;
+ uint8_t crystal_cap;
+#define R23B_ROM_CRYSTALCAP_DEF 0x20
+
+ uint8_t thermal_meter;
+#define R23B_ROM_THERMALMETER_DEF 0x18
+
+ uint8_t iqk_lck;
+ uint8_t pa_type;
+ uint8_t lna_type_2g;
+ uint8_t reserved2;
+ uint8_t lna_type_5g;
+ uint8_t reserved3;
+ uint8_t rf_board_opt;
+ uint8_t rf_feature_opt;
+ uint8_t rf_bt_opt;
+ uint8_t version;
+ uint8_t customer_id;
+ uint8_t tx_bswing_2g;
+ uint8_t reserved4;
+ uint8_t tx_pwr_calib_rate;
+ uint8_t rf_antenna_opt;
+ uint8_t rfe_opt;
+ uint8_t country_code;
+
+ struct {
+ uint8_t reserved1[52];
+ uint16_t vid;
+ uint16_t pid;
+ uint8_t reserved2[3];
+ uint8_t macaddr[IEEE80211_ADDR_LEN];
+ uint8_t reserved3[243];
+ } __packed usb;
+} __packed;
+
+_Static_assert(sizeof(struct r23b_rom) == R23B_EFUSE_MAP_LEN,
+ "R23B_EFUSE_MAP_LEN must be equal to sizeof(struct r23b_rom)!");
+
+#endif /* R23B_ROM_IMAGE_H */
diff --git a/sys/dev/rtwn/rtl8723b/r23b_rx.c b/sys/dev/rtwn/rtl8723b/r23b_rx.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/rtwn/rtl8723b/r23b_rx.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2026 Ahmad Khalifa <vexeduxr@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/param.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+#include <sys/taskqueue.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/linker.h>
+
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <net/if_media.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_radiotap.h>
+
+#include <dev/rtwn/if_rtwnreg.h>
+#include <dev/rtwn/if_rtwnvar.h>
+
+#include <dev/rtwn/rtl8188e/r88e_rx_desc.h>
+
+#include <dev/rtwn/rtl8723b/r23b.h>
+
+int8_t
+r23b_get_rssi_cck(struct rtwn_softc *sc, void *physt)
+{
+ struct r88e_rx_phystat *phy = (struct r88e_rx_phystat *)physt;
+ int8_t lna_idx, vga_idx, rssi;
+
+ lna_idx = (phy->agc_rpt & 0xe0) >> 5;
+ vga_idx = (phy->agc_rpt & 0x1f);
+
+ rssi = -2 * vga_idx;
+ switch (lna_idx) {
+ case 6:
+ rssi -= 34;
+ break;
+ case 4:
+ rssi -= 14;
+ break;
+ case 1:
+ rssi += 6;
+ break;
+ case 0:
+ rssi += 16;
+ break;
+ default:
+ rssi = 0;
+ break;
+ }
+
+ return (rssi);
+}
diff --git a/sys/dev/rtwn/rtl8723b/r23b_var.h b/sys/dev/rtwn/rtl8723b/r23b_var.h
new file mode 100644
--- /dev/null
+++ b/sys/dev/rtwn/rtl8723b/r23b_var.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2026 Ahmad Khalifa <vexeduxr@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef R23B_VAR_H
+#define R23B_VAR_H
+
+#include <dev/rtwn/rtl8723b/r23b_rom_defs.h>
+
+struct r23b_softc {
+ struct r92e_softc super;
+
+ uint8_t bt_antnum;
+};
+
+#endif
diff --git a/sys/dev/rtwn/rtl8723b/usb/r23bu.h b/sys/dev/rtwn/rtl8723b/usb/r23bu.h
new file mode 100644
--- /dev/null
+++ b/sys/dev/rtwn/rtl8723b/usb/r23bu.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2026 Ahmad Khalifa <vexeduxr@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef RTL8723BU_H
+#define RTL8723BU_H
+
+#include <dev/rtwn/rtl8723b/r23b.h>
+
+/* r23bu_init.c */
+int r23bu_power_on(struct rtwn_softc *);
+void r23bu_power_off(struct rtwn_softc *);
+void r23bu_init_bb(struct rtwn_softc *);
+void r23bu_init_ampdu(struct rtwn_softc *);
+void r23bu_init_rx_agg(struct rtwn_softc *sc);
+
+/* r23bu_rom.c */
+void r23bu_parse_rom(struct rtwn_softc *, uint8_t *);
+
+#endif
diff --git a/sys/dev/rtwn/rtl8723b/usb/r23bu_attach.c b/sys/dev/rtwn/rtl8723b/usb/r23bu_attach.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/rtwn/rtl8723b/usb/r23bu_attach.c
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2026 Ahmad Khalifa <vexeduxr@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/param.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+#include <sys/taskqueue.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/linker.h>
+
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <net/if_media.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_radiotap.h>
+
+#include <dev/rtwn/if_rtwnreg.h>
+#include <dev/rtwn/if_rtwnvar.h>
+#include <dev/rtwn/if_rtwn_nop.h>
+
+#include <dev/rtwn/usb/rtwn_usb_var.h>
+
+#include <dev/rtwn/rtl8188e/r88e.h>
+
+#include <dev/rtwn/rtl8192c/r92c.h>
+#include <dev/rtwn/rtl8192c/r92c_reg.h>
+
+#include <dev/rtwn/rtl8192c/usb/r92cu.h>
+
+#include <dev/rtwn/rtl8192e/r92e.h>
+#include <dev/rtwn/rtl8192e/r92e_var.h>
+
+#include <dev/rtwn/rtl8723b/r23b.h>
+#include <dev/rtwn/rtl8723b/r23b_var.h>
+#include <dev/rtwn/rtl8723b/r23b_priv.h>
+
+#include <dev/rtwn/rtl8723b/usb/r23bu.h>
+
+#include <dev/rtwn/rtl8812a/r12a_tx_desc.h>
+
+#include <dev/rtwn/rtl8812a/usb/r12au.h>
+
+#include <dev/rtwn/rtl8821a/r21a_reg.h>
+
+#include <dev/rtwn/rtl8821a/usb/r21au.h>
+
+void r23bu_attach(struct rtwn_usb_softc *);
+
+static void
+r23bu_attach_private(struct rtwn_softc *sc)
+{
+ struct r23b_softc *rs;
+
+ sc->sc_priv = rs = malloc(sizeof(struct r23b_softc),
+ M_RTWN_PRIV, M_WAITOK | M_ZERO);
+
+ rs->super.ac_usb_dma_size = 0x3;
+ rs->super.ac_usb_dma_time = 0x20;
+}
+
+static void
+r23bu_detach_private(struct rtwn_softc *sc)
+{
+ struct r23b_softc *rs = sc->sc_priv;
+
+ free(rs, M_RTWN_PRIV);
+}
+
+void
+r23bu_attach(struct rtwn_usb_softc *uc)
+{
+ struct rtwn_softc *sc = &uc->uc_sc;
+
+ /* USB part. */
+ uc->uc_align_rx = r12au_align_rx;
+ uc->tx_agg_desc_num = 0x6;
+
+ /* Common part. */
+ sc->sc_flags = RTWN_FLAG_EXT_HDR;
+
+ sc->sc_set_chan = r92e_set_chan;
+ sc->sc_fill_tx_desc = r12a_fill_tx_desc;
+ sc->sc_fill_tx_desc_raw = r12a_fill_tx_desc_raw;
+ sc->sc_fill_tx_desc_null = r12a_fill_tx_desc_null;
+ sc->sc_dump_tx_desc = r12au_dump_tx_desc;
+ sc->sc_tx_radiotap_flags = r12a_tx_radiotap_flags;
+ sc->sc_rx_radiotap_flags = r12a_rx_radiotap_flags;
+ sc->sc_get_rx_stats = r12a_get_rx_stats;
+ sc->sc_get_rssi_cck = r23b_get_rssi_cck;
+ sc->sc_get_rssi_ofdm = r88e_get_rssi_ofdm;
+ sc->sc_classify_intr = r12au_classify_intr;
+ sc->sc_handle_tx_report = r12a_ratectl_tx_complete;
+ sc->sc_handle_tx_report2 = rtwn_nop_softc_uint8_int;
+ sc->sc_handle_c2h_report = r92e_handle_c2h_report;
+ sc->sc_check_frame = rtwn_nop_int_softc_mbuf;
+ sc->sc_rf_read = r92e_rf_read;
+ sc->sc_rf_write = r23b_rf_write;
+ sc->sc_check_condition = r92c_check_condition;
+ sc->sc_efuse_postread = rtwn_nop_softc;
+ sc->sc_efuse_preread = r23b_efuse_preread;
+ sc->sc_parse_rom = r23bu_parse_rom;
+ sc->sc_set_led = r23b_set_led;
+ sc->sc_power_on = r23bu_power_on;
+ sc->sc_power_off = r23bu_power_off;
+#ifndef RTWN_WITHOUT_UCODE
+ sc->sc_fw_reset = r23b_fw_reset;
+ sc->sc_fw_download_enable = r23b_fw_download_enable;
+#endif
+ sc->sc_llt_init = r92e_llt_init;
+ sc->sc_set_page_size = r23b_set_page_size;
+ sc->sc_lc_calib = r23b_lc_calib;
+ sc->sc_iq_calib = r23b_iq_calib;
+ sc->sc_read_chipid_vendor = rtwn_nop_softc_uint32;
+ sc->sc_adj_devcaps = rtwn_nop_softc;
+ sc->sc_vap_preattach = rtwn_nop_softc_vap;
+ sc->sc_postattach = rtwn_nop_softc;
+ sc->sc_detach_private = r23bu_detach_private;
+#ifndef RTWN_WITHOUT_UCODE
+ sc->sc_set_media_status = r12a_set_media_status;
+ sc->sc_set_rsvd_page = r88e_set_rsvd_page;
+ sc->sc_set_pwrmode = r12a_set_pwrmode;
+ sc->sc_set_rssi = rtwn_nop_softc; /* XXX TODO */
+#else
+ sc->sc_set_media_status = rtwn_nop_softc_int;
+#endif
+ sc->sc_beacon_init = r12a_beacon_init;
+ sc->sc_beacon_enable = r92c_beacon_enable;
+ sc->sc_sta_beacon_enable = r12a_sta_beacon_enable;
+ sc->sc_beacon_set_rate = r12a_beacon_set_rate;
+ sc->sc_beacon_select = r21a_beacon_select;
+ sc->sc_temp_measure = r23b_temp_measure;
+ sc->sc_temp_read = r88e_temp_read;
+ sc->sc_init_tx_agg = r21au_init_tx_agg;
+ sc->sc_init_rx_agg = r23bu_init_rx_agg;
+ sc->sc_init_ampdu = r23bu_init_ampdu;
+ sc->sc_init_intr = r92cu_init_intr;
+ sc->sc_init_edca = r92c_init_edca;
+ sc->sc_init_bb = r23bu_init_bb;
+ sc->sc_init_rf = r23b_init_rf;
+ sc->sc_init_antsel = r23b_init_antsel;
+ sc->sc_post_init = r23b_post_init;
+ sc->sc_init_bcnq1_boundary = rtwn_nop_int_softc;
+ sc->sc_set_tx_power = r92e_set_tx_power;
+
+ sc->mac_prog = &rtl8723b_mac[0];
+ sc->mac_size = nitems(rtl8723b_mac);
+ sc->bb_prog = &rtl8723b_bb[0];
+ sc->bb_size = nitems(rtl8723b_bb);
+ sc->agc_prog = &rtl8723b_agc[0];
+ sc->agc_size = nitems(rtl8723b_agc);
+ sc->rf_prog = &rtl8723b_rf[0];
+
+ sc->name = "RTL8723BU";
+ sc->fwname = "rtwn-rtl8723bufw";
+ sc->fwsig = 0x530;
+
+ sc->page_count = R23B_TX_PAGE_COUNT;
+ sc->pktbuf_count = 0; /* Unused */
+ sc->ackto = 0x40;
+
+ sc->npubqpages = R23B_PUBQ_NPAGES;
+ sc->nhqpages = R23B_HPQ_NPAGES;
+ sc->nlqpages = R23B_LPQ_NPAGES;
+ sc->nnqpages = R23B_NPQ_NPAGES;
+
+ sc->page_size = R92C_TX_PAGE_SIZE;
+ sc->txdesc_len = sizeof(struct r12a_tx_desc);
+ sc->efuse_maxlen = R23B_EFUSE_MAX_LEN;
+ sc->efuse_maplen = R23B_EFUSE_MAP_LEN;
+ sc->rx_dma_size = R23B_RX_DMA_BUFFER_SIZE;
+
+ sc->macid_limit = R12A_MACID_MAX + 1;
+ sc->macid_rpt2_max_num = 2;
+ sc->cam_entry_limit = R12A_CAM_ENTRY_COUNT;
+ sc->fwsize_limit = R12A_MAX_FW_SIZE;
+ sc->temp_delta = R23B_CALIB_THRESHOLD;
+
+ sc->bcn_status_reg[0] = R92C_TDECTRL;
+ sc->bcn_status_reg[1] = R21A_DWBCN1_CTRL;
+ sc->rcr = 0;
+
+ sc->ntxchains = 1;
+ sc->nrxchains = 1;
+
+ r23bu_attach_private(sc);
+}
diff --git a/sys/dev/rtwn/rtl8723b/usb/r23bu_init.c b/sys/dev/rtwn/rtl8723b/usb/r23bu_init.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/rtwn/rtl8723b/usb/r23bu_init.c
@@ -0,0 +1,364 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2016 Andriy Voskoboinyk <avos@FreeBSD.org>
+ * Copyright (c) 2026 Ahmad Khalifa <vexeduxr@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+#include <sys/taskqueue.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/linker.h>
+
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <net/if_media.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_radiotap.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#include <dev/rtwn/if_rtwnreg.h>
+#include <dev/rtwn/if_rtwnvar.h>
+
+#include <dev/rtwn/usb/rtwn_usb_var.h>
+
+#include <dev/rtwn/rtl8188e/r88e_reg.h>
+
+#include <dev/rtwn/rtl8192c/r92c_reg.h>
+
+#include <dev/rtwn/rtl8192e/r92e_var.h>
+
+#include <dev/rtwn/rtl8723b/r23b_reg.h>
+#include <dev/rtwn/rtl8723b/r23b_var.h>
+
+#include <dev/rtwn/rtl8723b/usb/r23bu.h>
+
+#include <dev/rtwn/rtl8812a/r12a_reg.h>
+
+int
+r23bu_power_on(struct rtwn_softc *sc)
+{
+#define RTWN_CHK(res) do { \
+ if (res != 0) \
+ return (EIO); \
+} while(0)
+ int ntries;
+
+ /* Clear suspend and power down bits.*/
+ RTWN_CHK(rtwn_setbits_1_shift(sc, R92C_APS_FSMCO,
+ R92C_APS_FSMCO_AFSM_HSUS | R92C_APS_FSMCO_APDM_HPDN, 0, 1));
+
+ /* Disable GPIO9 as EXT WAKEUP. */
+ RTWN_CHK(rtwn_setbits_1(sc, R92C_GPIO_INTM + 2, 0x01, 0));
+
+ /* Enable WL suspend. */
+ RTWN_CHK(rtwn_setbits_1_shift(sc, R92C_APS_FSMCO,
+ R92C_APS_FSMCO_AFSM_HSUS | R92C_APS_FSMCO_AFSM_PCIE, 0, 1));
+
+ /* Enable LDOA12 MACRO block for all interfaces. */
+ RTWN_CHK(rtwn_setbits_1(sc, R92C_LDOA15_CTRL, 0, R92C_LDOA15_CTRL_EN));
+
+ /* Disable BT_GPS_SEL pins. */
+ RTWN_CHK(rtwn_setbits_1(sc, 0x067, 0x10, 0));
+
+ /* 1 ms delay. */
+ rtwn_delay(sc, 1000);
+
+ /* Release analog Ips to digital isolation. */
+ RTWN_CHK(rtwn_setbits_1(sc, R92C_SYS_ISO_CTRL,
+ R92C_SYS_ISO_CTRL_IP2MAC, 0));
+
+ /* Disable SW LPS and WL suspend. */
+ RTWN_CHK(rtwn_setbits_1_shift(sc, R92C_APS_FSMCO,
+ R92C_APS_FSMCO_APFM_RSM |
+ R92C_APS_FSMCO_AFSM_HSUS |
+ R92C_APS_FSMCO_AFSM_PCIE, 0, 1));
+
+ /* Wait for power ready bit. */
+ for (ntries = 0; ntries < 5000; ntries++) {
+ if (rtwn_read_4(sc, R92C_APS_FSMCO) & R92C_APS_FSMCO_SUS_HOST)
+ break;
+ rtwn_delay(sc, 10);
+ }
+ if (ntries == 5000) {
+ device_printf(sc->sc_dev,
+ "timeout waiting for chip power up\n");
+ return (ETIMEDOUT);
+ }
+
+ /* Release WLON reset. */
+ RTWN_CHK(rtwn_setbits_1_shift(sc, R92C_APS_FSMCO, 0,
+ R92C_APS_FSMCO_RDY_MACON, 2));
+
+ /* Disable HWPDN. */
+ RTWN_CHK(rtwn_setbits_1_shift(sc, R92C_APS_FSMCO,
+ R92C_APS_FSMCO_APDM_HPDN, 0, 1));
+
+ /* Disable WL suspend. */
+ RTWN_CHK(rtwn_setbits_1_shift(sc, R92C_APS_FSMCO,
+ R92C_APS_FSMCO_AFSM_HSUS | R92C_APS_FSMCO_AFSM_PCIE, 0, 1));
+
+ RTWN_CHK(rtwn_setbits_1_shift(sc, R92C_APS_FSMCO, 0,
+ R92C_APS_FSMCO_APFM_ONMAC, 1));
+ for (ntries = 0; ntries < 5000; ntries++) {
+ if (!(rtwn_read_2(sc, R92C_APS_FSMCO) &
+ R92C_APS_FSMCO_APFM_ONMAC))
+ break;
+ rtwn_delay(sc, 10);
+ }
+ if (ntries == 5000)
+ return (ETIMEDOUT);
+
+ RTWN_CHK(rtwn_setbits_1(sc, R92C_AFE_MISC, 0, 0x40));
+
+ /* Enable falling edge triggering interrupt. */
+ RTWN_CHK(rtwn_setbits_1(sc, R92C_GPIO_INTM + 1, 0, 0x02));
+
+ /* Enable GPIO9 interrupt mode. */
+ RTWN_CHK(rtwn_setbits_1(sc, 0x063, 0, 0x02));
+
+ /* Enable GPIO9 input mode. */
+ RTWN_CHK(rtwn_setbits_1(sc, 0x062, 0x02, 0));
+
+ /* Enable HSISR GPIO interrupt. */
+ RTWN_CHK(rtwn_setbits_1(sc, R92C_HSIMR, 0, 0x01));
+
+ /* Enable HSISR GPIO9 interrupt. */
+ RTWN_CHK(rtwn_setbits_1(sc, R92C_HSIMR + 2, 0, 0x02));
+
+ /* GPIO9 internal pull up. */
+ RTWN_CHK(rtwn_setbits_1(sc, R92C_MULTI_FUNC_CTRL, 0, 0x08));
+ RTWN_CHK(rtwn_setbits_1_shift(sc, R92C_MULTI_FUNC_CTRL, 0, 0x4000, 1));
+
+ /* Enable MAC DMA/WMAC/SCHEDULE/SEC blocks. */
+ RTWN_CHK(rtwn_write_1(sc, R92C_CR, 0x00));
+ RTWN_CHK(rtwn_setbits_2(sc, R92C_CR, 0,
+ R92C_CR_HCI_TXDMA_EN | R92C_CR_TXDMA_EN |
+ R92C_CR_HCI_RXDMA_EN | R92C_CR_RXDMA_EN |
+ R92C_CR_PROTOCOL_EN | R92C_CR_SCHEDULE_EN |
+ ((sc->sc_hwcrypto != RTWN_CRYPTO_SW) ? R92C_CR_ENSEC : 0) |
+ R92C_CR_CALTMR_EN));
+
+ /* btcoex power on. */
+
+ RTWN_CHK(rtwn_write_1(sc, R88E_BB_PAD_CTRL + 3, 0x20));
+ RTWN_CHK(rtwn_setbits_2(sc, R92C_SYS_FUNC_EN, 0,
+ R92C_SYS_FUNC_EN_BBRSTB | R92C_SYS_FUNC_EN_BB_GLB_RST));
+ RTWN_CHK(rtwn_write_1(sc, R23B_GNT_BT + 1, 0x18));
+ RTWN_CHK(rtwn_write_1(sc, 0x76e, 0x4));
+ RTWN_CHK(rtwn_write_4(sc, R23B_ANT_SEL, R23B_ANT_SEL_S0));
+ RTWN_CHK(rtwn_write_1(sc, 0xfe08, 0x1));
+
+ RTWN_CHK(rtwn_setbits_2(sc, R92C_PWR_DATA, 0,
+ R23B_PWR_DATA_RFE_CTRL_EN));
+
+ RTWN_CHK(rtwn_setbits_4(sc, R92C_LEDCFG0, 0, R23B_LEDCFG0_DPDT_SEL_EN));
+
+ RTWN_CHK(rtwn_setbits_1(sc, R88E_BB_PAD_CTRL,
+ R23B_BB_PAD_CTRL_DPDT_SEL_DATA, 0));
+
+ return (0);
+#undef RTWN_CHK
+}
+
+void
+r23bu_power_off(struct rtwn_softc *sc)
+{
+ int error, ntries;
+
+ /* Stop Tx report timer. */
+ error = rtwn_setbits_1(sc, R88E_TX_RPT_CTRL, R88E_TX_RPT2_ENA, 0);
+ if (error == ENXIO) /* hardware gone */
+ return;
+
+ /* Stop Rx. */
+ rtwn_write_4(sc, R92C_CR, 0x0000);
+
+ /* Move card to Low Power state. */
+ /* Block all Tx queues. */
+ rtwn_write_1(sc, R92C_TXPAUSE, R92C_TX_QUEUE_ALL);
+
+ for (ntries = 0; ntries < 10; ntries++) {
+ /* Should be zero if no packet is transmitting. */
+ if (rtwn_read_4(sc, R88E_SCH_TXCMD) == 0)
+ break;
+
+ rtwn_delay(sc, 5000);
+ }
+ if (ntries == 10) {
+ device_printf(sc->sc_dev, "%s: failed to block Tx queues\n",
+ __func__);
+ return;
+ }
+
+ /* CCK and OFDM are disabled, and clock are gated. */
+ rtwn_setbits_1(sc, R92C_SYS_FUNC_EN, R92C_SYS_FUNC_EN_BBRSTB, 0);
+
+ rtwn_delay(sc, 1);
+
+ /* Reset whole BB. */
+ rtwn_setbits_1(sc, R92C_SYS_FUNC_EN, R92C_SYS_FUNC_EN_BB_GLB_RST, 0);
+
+ /* Reset MAC TRX. */
+ rtwn_write_1(sc, R92C_CR, R92C_CR_HCI_TXDMA_EN | R92C_CR_HCI_RXDMA_EN);
+
+ /* check if removed later. (?) */
+ rtwn_setbits_1_shift(sc, R92C_CR, R92C_CR_ENSEC, 0, 1);
+
+ /* Respond TxOK to scheduler */
+ rtwn_setbits_1(sc, R92C_DUAL_TSF_RST, 0, R92C_DUAL_TSF_RST_TXOK);
+
+#ifndef RTWN_WITHOUT_UCODE
+ if ((sc->sc_flags & RTWN_FW_LOADED) &&
+ (rtwn_read_1(sc, R92C_MCUFWDL) & R92C_MCUFWDL_RAM_DL_SEL))
+ rtwn_fw_reset(sc, RTWN_FW_RESET_SHUTDOWN);
+#endif
+
+ /* Reset MCU. */
+ rtwn_setbits_1_shift(sc, R92C_SYS_FUNC_EN, R92C_SYS_FUNC_EN_CPUEN,
+ 0, 1);
+ rtwn_write_1(sc, R92C_MCUFWDL, 0);
+
+ /* Move card to Disabled state. */
+ /* Turn off RF. */
+ rtwn_write_1(sc, R92C_RF_CTRL, 0);
+
+ /* Enable rising edge triggering interrupt. */
+ rtwn_setbits_1(sc, R92C_GPIO_INTM + 1, 0x02, 0);
+
+ /* Release WLON reset. */
+ rtwn_setbits_1_shift(sc, R92C_APS_FSMCO, 0,
+ R92C_APS_FSMCO_RDY_MACON, 2);
+
+ /* Turn off MAC by HW state machine */
+ rtwn_setbits_1_shift(sc, R92C_APS_FSMCO, 0, R92C_APS_FSMCO_APFM_OFF, 1);
+ for (ntries = 0; ntries < 10; ntries++) {
+ /* Wait until it will be disabled. */
+ if ((rtwn_read_2(sc, R92C_APS_FSMCO) &
+ R92C_APS_FSMCO_APFM_OFF) == 0)
+ break;
+
+ rtwn_delay(sc, 5000);
+ }
+ if (ntries == 10) {
+ device_printf(sc->sc_dev, "%s: could not turn off MAC\n",
+ __func__);
+ return;
+ }
+
+ rtwn_setbits_1(sc, R92C_AFE_MISC, 0x40, 0);
+
+ /* Analog Ips to digital isolation. */
+ rtwn_setbits_1(sc, R92C_SYS_ISO_CTRL, 0, R92C_SYS_ISO_CTRL_IP2MAC);
+
+ /* Disable LDOA12 MACRO block. */
+ rtwn_setbits_1(sc, R92C_LDOA15_CTRL, R92C_LDOA15_CTRL_EN, 0);
+
+ /* Enable WL suspend. */
+ rtwn_setbits_1_shift(sc, R92C_APS_FSMCO, R92C_APS_FSMCO_AFSM_PCIE,
+ R92C_APS_FSMCO_AFSM_HSUS, 1);
+
+ /* Enable GPIO9 as EXT WAKEUP. */
+ rtwn_setbits_1(sc, R92C_GPIO_INTM + 2, 0, 0x01);
+}
+
+void
+r23bu_init_bb(struct rtwn_softc *sc)
+{
+ /* Enable BB and RF. */
+ rtwn_setbits_2(sc, R92C_SYS_FUNC_EN, 0, R92C_SYS_FUNC_EN_BBRSTB |
+ R92C_SYS_FUNC_EN_BB_GLB_RST | R92C_SYS_FUNC_EN_DIO_RF);
+
+ /* Select antenna S0. */
+ rtwn_write_4(sc, R23B_ANT_SEL, R23B_ANT_SEL_S0);
+
+ return (r23b_init_bb_common(sc));
+}
+
+void
+r23bu_init_rx_agg(struct rtwn_softc *sc)
+{
+ struct r23b_softc *rs = sc->sc_priv;
+ uint32_t val;
+
+ rtwn_setbits_1(sc, R92C_TRXDMA_CTRL, 0, R92C_TRXDMA_CTRL_RXDMA_AGG_EN);
+
+ val = rtwn_read_4(sc, R92C_RXDMA_AGG_PG_TH);
+ val = RW(val, R23B_RXDMA_AGG_PG_TH_SIZE, rs->super.ac_usb_dma_size);
+ val = RW(val, R23B_RXDMA_AGG_PG_TH_TIME, rs->super.ac_usb_dma_time);
+ val |= R23B_RXDMA_AGG_PG_TH_EN;
+ rtwn_write_4(sc, R92C_RXDMA_AGG_PG_TH, val);
+}
+
+void
+r23bu_init_ampdu(struct rtwn_softc *sc)
+{
+ struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
+ uint8_t val;
+
+ val = rtwn_read_1(sc, R12A_RXDMA_PRO);
+ val = RW(val, R12A_BURST_SZ,
+ usbd_get_speed(uc->uc_udev) == USB_SPEED_HIGH ?
+ R12A_BURST_SZ_USB2 : R12A_BURST_SZ_USB1);
+ val |= R12A_DMA_MODE | SM(R12A_BURST_CNT, 3);
+ rtwn_write_1(sc, R12A_RXDMA_PRO, val);
+
+ /* Enable single packet AMPDU. */
+ rtwn_setbits_1(sc, R12A_HT_SINGLE_AMPDU, 0,
+ R12A_HT_SINGLE_AMPDU_PKT_ENA);
+
+ rtwn_write_2(sc, R92C_MAX_AGGR_NUM, 0x0c14);
+ rtwn_write_1(sc, R12A_AMPDU_MAX_TIME, 0x5e);
+ rtwn_write_4(sc, R12A_AMPDU_MAX_LENGTH, UINT32_MAX);
+
+ /*
+ * The vendor driver says this is for
+ * "VHT packet length 11K", but this chip
+ * doesn't support VHT rates...
+ */
+ rtwn_write_1(sc, R92C_RX_PKT_LIMIT, 0x18);
+
+ rtwn_write_1(sc, R92C_PIFS, 0);
+ rtwn_write_1(sc, R92C_FWHW_TXQ_CTRL, R92C_FWHW_TXQ_CTRL_AMPDU_RTY_NEW);
+ rtwn_write_4(sc, R92C_FAST_EDCA_CTRL, 0x03086666);
+
+ rtwn_write_1(sc, R92C_USTIME_TSF, 0x50);
+ rtwn_write_1(sc, R92C_USTIME_EDCA, 0x50);
+
+ /* Do not reset MAC. */
+ rtwn_setbits_1(sc, R92C_RSV_CTRL, 0, 0x60);
+}
diff --git a/sys/dev/rtwn/rtl8723b/usb/r23bu_rom.c b/sys/dev/rtwn/rtl8723b/usb/r23bu_rom.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/rtwn/rtl8723b/usb/r23bu_rom.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2026 Ahmad Khalifa <vexeduxr@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/param.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+#include <sys/taskqueue.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/linker.h>
+
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <net/if_media.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_radiotap.h>
+
+#include <dev/rtwn/if_rtwnreg.h>
+#include <dev/rtwn/if_rtwnvar.h>
+
+#include <dev/rtwn/rtl8723b/r23b.h>
+#include <dev/rtwn/rtl8723b/r23b_rom_image.h>
+
+#include <dev/rtwn/rtl8723b/usb/r23bu.h>
+
+void
+r23bu_parse_rom(struct rtwn_softc *sc, uint8_t *buf)
+{
+ struct r23b_rom *rom = (struct r23b_rom *)buf;
+
+ r23b_parse_rom(sc, buf);
+ IEEE80211_ADDR_COPY(sc->sc_ic.ic_macaddr, rom->usb.macaddr);
+}
diff --git a/sys/dev/rtwn/rtl8812a/r12a_tx_desc.h b/sys/dev/rtwn/rtl8812a/r12a_tx_desc.h
--- a/sys/dev/rtwn/rtl8812a/r12a_tx_desc.h
+++ b/sys/dev/rtwn/rtl8812a/r12a_tx_desc.h
@@ -115,6 +115,12 @@
#define R12A_TXDW5_RTS_PRIM_CHAN_M 0x0001e000
#define R12A_TXDW5_RTS_PRIM_CHAN_S 13
+ /*
+ * TODO: some chips (at least the 23bu)
+ * let us pass arbitrary data in the first
+ * 12 bits that is persisted through to the
+ * TX report.
+ */
uint32_t txdw6;
#define R21A_TXDW6_MBSSID_M 0x0000f000
#define R21A_TXDW6_MBSSID_S 12
diff --git a/sys/dev/rtwn/usb/rtwn_usb_attach.h b/sys/dev/rtwn/usb/rtwn_usb_attach.h
--- a/sys/dev/rtwn/usb/rtwn_usb_attach.h
+++ b/sys/dev/rtwn/usb/rtwn_usb_attach.h
@@ -21,6 +21,7 @@
void r92cu_attach(struct rtwn_usb_softc *);
void r92eu_attach(struct rtwn_usb_softc *);
void r88eu_attach(struct rtwn_usb_softc *);
+void r23bu_attach(struct rtwn_usb_softc *);
void r12au_attach(struct rtwn_usb_softc *);
void r21au_attach(struct rtwn_usb_softc *);
@@ -28,6 +29,7 @@
RTWN_CHIP_RTL8192CU,
RTWN_CHIP_RTL8192EU,
RTWN_CHIP_RTL8188EU,
+ RTWN_CHIP_RTL8723BU,
RTWN_CHIP_RTL8812AU,
RTWN_CHIP_RTL8821AU,
RTWN_CHIP_MAX_USB
@@ -132,6 +134,14 @@
RTWN_RTL8188EU_DEV(MERCUSYS, MW150US),
#undef RTWN_RTL8188EU_DEV
+ /* RTL8723BU */
+#define RTWN_RTL8723BU_DEV(v, p, i) \
+ { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p), \
+ USB_IF_CSI(UICLASS_VENDOR, UISUBCLASS_VENDOR, \
+ RTWN_USB_DEVINFO(RTWN_CHIP_RTL8723BU, i)) }
+ RTWN_RTL8723BU_DEV(REALTEK, RTL8723BU, 2),
+#undef RTWN_RTL8723BU_DEV
+
/* RTL8812AU */
#define RTWN_RTL8812AU_DEV(v,p) \
{ USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, \
@@ -185,6 +195,7 @@
[RTWN_CHIP_RTL8192CU] = r92cu_attach,
[RTWN_CHIP_RTL8192EU] = r92eu_attach,
[RTWN_CHIP_RTL8188EU] = r88eu_attach,
+ [RTWN_CHIP_RTL8723BU] = r23bu_attach,
[RTWN_CHIP_RTL8812AU] = r12au_attach,
[RTWN_CHIP_RTL8821AU] = r21au_attach
};
diff --git a/sys/modules/rtwn/Makefile b/sys/modules/rtwn/Makefile
--- a/sys/modules/rtwn/Makefile
+++ b/sys/modules/rtwn/Makefile
@@ -27,6 +27,11 @@
r92e_rom.c r92e_rx.c r92e.h r92e_priv.h r92e_reg.h \
r92e_rom_image.h r92e_rom_defs.h
+.PATH: ${SRCTOP}/sys/dev/rtwn/rtl8723b
+SRCS += r23b_calib.c r23b_fw.c r23b_init.c r23b_led.c r23b_rf.c \
+ r23b_rom.c r23b_rx.c r23b.h r23b_priv.h r23b_reg.h \
+ r23b_var.h r23b_rom_defs.h r23b_rom_image.h r23b_fw_cmd.h
+
.PATH: ${SRCTOP}/sys/dev/rtwn/rtl8812a
SRCS += r12a_beacon.c r12a_calib.c r12a_caps.c r12a_chan.c r12a_fw.c \
r12a_init.c r12a_led.c r12a_rf.c r12a_rom.c r12a_rx.c r12a_tx.c \
diff --git a/sys/modules/rtwn_usb/Makefile b/sys/modules/rtwn_usb/Makefile
--- a/sys/modules/rtwn_usb/Makefile
+++ b/sys/modules/rtwn_usb/Makefile
@@ -19,6 +19,9 @@
.PATH: ${SRCTOP}/sys/dev/rtwn/rtl8192e/usb
SRCS += r92eu_attach.c r92eu_init.c r92eu.h r92eu_reg.h
+.PATH: ${SRCTOP}/sys/dev/rtwn/rtl8723b/usb
+SRCS += r23bu_attach.c r23bu_init.c r23bu_rom.c r23bu.h
+
.PATH: ${SRCTOP}/sys/dev/rtwn/rtl8812a/usb
SRCS += r12au_attach.c r12au_init.c r12au_rx.c r12au_tx.c \
r12au.h r12au_reg.h r12au_tx_desc.h
diff --git a/sys/modules/rtwnfw/Makefile b/sys/modules/rtwnfw/Makefile
--- a/sys/modules/rtwnfw/Makefile
+++ b/sys/modules/rtwnfw/Makefile
@@ -1,4 +1,4 @@
SUBDIR= rtwnrtl8188eu rtwnrtl8192cT rtwnrtl8192cU rtwnrtl8192eu rtwnrtl8812au \
- rtwnrtl8821au rtwnrtl8192cE rtwnrtl8192cEB rtwnrtl8188ee
+ rtwnrtl8821au rtwnrtl8192cE rtwnrtl8192cEB rtwnrtl8188ee rtwnrtl8723bu
.include <bsd.subdir.mk>
diff --git a/sys/modules/rtwnfw/rtwnrtl8723bu/Makefile b/sys/modules/rtwnfw/rtwnrtl8723bu/Makefile
new file mode 100644
--- /dev/null
+++ b/sys/modules/rtwnfw/rtwnrtl8723bu/Makefile
@@ -0,0 +1,4 @@
+KMOD= rtwn-rtl8723bufw
+IMG= rtwn-rtl8723bufw
+
+.include <bsd.kmod.mk>
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Sep 7, 9:13 PM (21 h, 7 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
38040566
Default Alt Text
D59205.diff (122 KB)
Attached To
Mode
D59205: rtwn: add support for the RTL8723BU
Attached
Detach File
Event Timeline
Log In to Comment