aboutsummaryrefslogtreecommitdiff
path: root/sys/compat/linuxkpi/common
diff options
context:
space:
mode:
Diffstat (limited to 'sys/compat/linuxkpi/common')
-rw-r--r--sys/compat/linuxkpi/common/include/asm/pgtable.h34
-rw-r--r--sys/compat/linuxkpi/common/include/asm/set_memory.h6
-rw-r--r--sys/compat/linuxkpi/common/include/linux/array_size.h17
-rw-r--r--sys/compat/linuxkpi/common/include/linux/ascii85.h46
-rw-r--r--sys/compat/linuxkpi/common/include/linux/atomic.h2
-rw-r--r--sys/compat/linuxkpi/common/include/linux/atomic/atomic-arch-fallback.h16
-rw-r--r--sys/compat/linuxkpi/common/include/linux/atomic/atomic-instrumented.h16
-rw-r--r--sys/compat/linuxkpi/common/include/linux/bcm47xx_nvram.h21
-rw-r--r--sys/compat/linuxkpi/common/include/linux/bcma/bcma.h29
-rw-r--r--sys/compat/linuxkpi/common/include/linux/bcma/bcma_regs.h17
-rw-r--r--sys/compat/linuxkpi/common/include/linux/bitops.h16
-rw-r--r--sys/compat/linuxkpi/common/include/linux/bits.h14
-rw-r--r--sys/compat/linuxkpi/common/include/linux/cleanup.h127
-rw-r--r--sys/compat/linuxkpi/common/include/linux/compiler.h3
-rw-r--r--sys/compat/linuxkpi/common/include/linux/compiler_types.h13
-rw-r--r--sys/compat/linuxkpi/common/include/linux/device.h31
-rw-r--r--sys/compat/linuxkpi/common/include/linux/dma-mapping.h21
-rw-r--r--sys/compat/linuxkpi/common/include/linux/eventfd.h54
-rw-r--r--sys/compat/linuxkpi/common/include/linux/file.h2
-rw-r--r--sys/compat/linuxkpi/common/include/linux/fs.h45
-rw-r--r--sys/compat/linuxkpi/common/include/linux/gfp.h8
-rw-r--r--sys/compat/linuxkpi/common/include/linux/hardirq.h1
-rw-r--r--sys/compat/linuxkpi/common/include/linux/hex.h44
-rw-r--r--sys/compat/linuxkpi/common/include/linux/highmem.h27
-rw-r--r--sys/compat/linuxkpi/common/include/linux/ieee80211.h31
-rw-r--r--sys/compat/linuxkpi/common/include/linux/instruction_pointer.h15
-rw-r--r--sys/compat/linuxkpi/common/include/linux/io.h4
-rw-r--r--sys/compat/linuxkpi/common/include/linux/ioport.h1
-rw-r--r--sys/compat/linuxkpi/common/include/linux/kconfig.h35
-rw-r--r--sys/compat/linuxkpi/common/include/linux/kdev_t.h15
-rw-r--r--sys/compat/linuxkpi/common/include/linux/kernel.h83
-rw-r--r--sys/compat/linuxkpi/common/include/linux/kfifo.h5
-rw-r--r--sys/compat/linuxkpi/common/include/linux/kmsg_dump.h51
-rw-r--r--sys/compat/linuxkpi/common/include/linux/linux_logo.h19
-rw-r--r--sys/compat/linuxkpi/common/include/linux/math64.h9
-rw-r--r--sys/compat/linuxkpi/common/include/linux/mfd/core.h49
-rw-r--r--sys/compat/linuxkpi/common/include/linux/minmax.h16
-rw-r--r--sys/compat/linuxkpi/common/include/linux/mm.h1
-rw-r--r--sys/compat/linuxkpi/common/include/linux/mod_devicetable.h7
-rw-r--r--sys/compat/linuxkpi/common/include/linux/module.h18
-rw-r--r--sys/compat/linuxkpi/common/include/linux/mutex.h2
-rw-r--r--sys/compat/linuxkpi/common/include/linux/netdevice.h24
-rw-r--r--sys/compat/linuxkpi/common/include/linux/overflow.h144
-rw-r--r--sys/compat/linuxkpi/common/include/linux/pci.h36
-rw-r--r--sys/compat/linuxkpi/common/include/linux/platform_data/brcmfmac.h185
-rw-r--r--sys/compat/linuxkpi/common/include/linux/ptp_clock_kernel.h1
-rw-r--r--sys/compat/linuxkpi/common/include/linux/radix-tree.h29
-rw-r--r--sys/compat/linuxkpi/common/include/linux/rbtree.h20
-rw-r--r--sys/compat/linuxkpi/common/include/linux/scatterlist.h4
-rw-r--r--sys/compat/linuxkpi/common/include/linux/seq_buf.h73
-rw-r--r--sys/compat/linuxkpi/common/include/linux/seq_file.h4
-rw-r--r--sys/compat/linuxkpi/common/include/linux/sizes.h11
-rw-r--r--sys/compat/linuxkpi/common/include/linux/skbuff.h9
-rw-r--r--sys/compat/linuxkpi/common/include/linux/slab.h67
-rw-r--r--sys/compat/linuxkpi/common/include/linux/soc/airoha/airoha_offload.h48
-rw-r--r--sys/compat/linuxkpi/common/include/linux/soc/mediatek/mtk_wed.h14
-rw-r--r--sys/compat/linuxkpi/common/include/linux/sort.h7
-rw-r--r--sys/compat/linuxkpi/common/include/linux/spinlock.h11
-rw-r--r--sys/compat/linuxkpi/common/include/linux/ssb/ssb_regs.h25
-rw-r--r--sys/compat/linuxkpi/common/include/linux/string.h40
-rw-r--r--sys/compat/linuxkpi/common/include/linux/string_choices.h26
-rw-r--r--sys/compat/linuxkpi/common/include/linux/suspend.h21
-rw-r--r--sys/compat/linuxkpi/common/include/linux/sysfs.h34
-rw-r--r--sys/compat/linuxkpi/common/include/linux/unaligned.h12
-rw-r--r--sys/compat/linuxkpi/common/include/linux/uuid.h27
-rw-r--r--sys/compat/linuxkpi/common/include/linux/wait.h7
-rw-r--r--sys/compat/linuxkpi/common/include/linux/wordpart.h13
-rw-r--r--sys/compat/linuxkpi/common/include/net/cfg80211.h95
-rw-r--r--sys/compat/linuxkpi/common/include/net/mac80211.h107
-rw-r--r--sys/compat/linuxkpi/common/src/linux_80211.c1980
-rw-r--r--sys/compat/linuxkpi/common/src/linux_80211.h16
-rw-r--r--sys/compat/linuxkpi/common/src/linux_80211_macops.c80
-rw-r--r--sys/compat/linuxkpi/common/src/linux_compat.c5
-rw-r--r--sys/compat/linuxkpi/common/src/linux_eventfd.c63
-rw-r--r--sys/compat/linuxkpi/common/src/linux_netdev.c2
-rw-r--r--sys/compat/linuxkpi/common/src/linux_page.c41
-rw-r--r--sys/compat/linuxkpi/common/src/linux_pci.c64
-rw-r--r--sys/compat/linuxkpi/common/src/linux_radix.c219
-rw-r--r--sys/compat/linuxkpi/common/src/linux_seq_buf.c64
-rw-r--r--sys/compat/linuxkpi/common/src/linux_seq_file.c70
-rw-r--r--sys/compat/linuxkpi/common/src/linux_simple_attr.c48
-rw-r--r--sys/compat/linuxkpi/common/src/linux_slab.c49
-rw-r--r--sys/compat/linuxkpi/common/src/linux_xarray.c4
83 files changed, 3617 insertions, 1153 deletions
diff --git a/sys/compat/linuxkpi/common/include/asm/pgtable.h b/sys/compat/linuxkpi/common/include/asm/pgtable.h
index 865662d587db..166d6142d51c 100644
--- a/sys/compat/linuxkpi/common/include/asm/pgtable.h
+++ b/sys/compat/linuxkpi/common/include/asm/pgtable.h
@@ -53,6 +53,40 @@ typedef struct page *pgtable_t;
#define _PAGE_PWT (((pteval_t) 1) << _PAGE_BIT_PWT)
#define _PAGE_PCD (((pteval_t) 1) << _PAGE_BIT_PCD)
#define _PAGE_PAT (((pteval_t) 1) << _PAGE_BIT_PAT)
+
+/*
+ * On Linux, the value of `PMD_SHIFT` is hard-coded to 21. This corresponds to
+ * the FreeBSD `PDRSHIFT` constant.
+ */
+#define PMD_SHIFT PDRSHIFT
+
+#elif defined(__aarch64__)
+
+/*
+ * On Linux, the value of `PMD_SHIFT` is computed from `CONFIG_PGTABLE_LEVELS`.
+ * The result corresponds to one of the FreeBSD `L*_SHIFT` constants. Here, we
+ * take the value 21 computed from `CONFIG_PGTABLE_LEVELS = 4`, the default on
+ * aarch64, which equals to `L2_SHIFT`.
+ */
+#define PMD_SHIFT L2_SHIFT
+
+#elif defined(__powerpc__)
+
+/*
+ * On Linux, the value of `PMD_SHIFT` is the addition of `PAGE_SHIFT` and
+ * `PTE_INDEX_SIZE` (hard-coded to 9). The result corresponds to the FreeBSD
+ * `L3_PAGE_SIZE_SHIFT` constant.
+ */
+#define PMD_SHIFT L3_PAGE_SIZE_SHIFT
+
+#elif defined(__riscv)
+
+/*
+ * On Linux, the value of `PMD_SHIFT` is hard-coded to 21. This corresponds to
+ * the FreeBSD `L2_SHIFT` constant.
+ */
+#define PMD_SHIFT L2_SHIFT
+
#endif
#endif /* _LINUXKPI_ASM_PGTABLE_H_ */
diff --git a/sys/compat/linuxkpi/common/include/asm/set_memory.h b/sys/compat/linuxkpi/common/include/asm/set_memory.h
index 1019aaf264a0..f328fcabd243 100644
--- a/sys/compat/linuxkpi/common/include/asm/set_memory.h
+++ b/sys/compat/linuxkpi/common/include/asm/set_memory.h
@@ -37,7 +37,7 @@ set_memory_uc(unsigned long addr, int numpages)
vm_size_t len;
len = (vm_size_t)numpages << PAGE_SHIFT;
- return (-pmap_change_attr(addr, len, VM_MEMATTR_UNCACHEABLE));
+ return (-pmap_change_attr((void *)addr, len, VM_MEMATTR_UNCACHEABLE));
}
static inline int
@@ -47,7 +47,7 @@ set_memory_wc(unsigned long addr, int numpages)
vm_size_t len;
len = (vm_size_t)numpages << PAGE_SHIFT;
- return (-pmap_change_attr(addr, len, VM_MEMATTR_WRITE_COMBINING));
+ return (-pmap_change_attr((void *)addr, len, VM_MEMATTR_WRITE_COMBINING));
#else
return (set_memory_uc(addr, numpages));
#endif
@@ -59,7 +59,7 @@ set_memory_wb(unsigned long addr, int numpages)
vm_size_t len;
len = (vm_size_t)numpages << PAGE_SHIFT;
- return (-pmap_change_attr(addr, len, VM_MEMATTR_WRITE_BACK));
+ return (-pmap_change_attr((void *)addr, len, VM_MEMATTR_WRITE_BACK));
}
static inline int
diff --git a/sys/compat/linuxkpi/common/include/linux/array_size.h b/sys/compat/linuxkpi/common/include/linux/array_size.h
new file mode 100644
index 000000000000..ebfaeccc9536
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/array_size.h
@@ -0,0 +1,17 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2010 Isilon Systems, Inc.
+ * Copyright (c) 2010 iX Systems, Inc.
+ * Copyright (c) 2010 Panasas, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _LINUXKPI_LINUX_ARRAY_SIZE_H_
+#define _LINUXKPI_LINUX_ARRAY_SIZE_H_
+
+#include <linux/compiler.h>
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+#endif /* _LINUXKPI_LINUX_ARRAY_SIZE_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/ascii85.h b/sys/compat/linuxkpi/common/include/linux/ascii85.h
new file mode 100644
index 000000000000..06777a130e41
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/ascii85.h
@@ -0,0 +1,46 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 The FreeBSD Foundation
+ */
+
+#ifndef _LINUXKPI_LINUX_ASCII85_H_
+#define _LINUXKPI_LINUX_ASCII85_H_
+
+#include <sys/param.h>
+
+#define ASCII85_BUFSZ 6
+
+static inline long
+ascii85_encode_len(long in_len)
+{
+ long out_len;
+
+ out_len = howmany(in_len, 4);
+
+ return (out_len);
+}
+
+static inline const char *
+ascii85_encode(uint32_t in, char *out)
+{
+ int i;
+
+ if (in == 0) {
+ out[0] = 'z';
+ out[1] = '\0';
+ return (out);
+ }
+
+ for (i = ASCII85_BUFSZ - 2; i >= 0; i--) {
+ out[i] = in % 85;
+ out[i] += 33;
+
+ in /= 85;
+ }
+ out[ASCII85_BUFSZ - 1] = '\0';
+
+ return (out);
+}
+
+#endif /* _LINUXKPI_LINUX_ASCII85_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/atomic.h b/sys/compat/linuxkpi/common/include/linux/atomic.h
index bc76928a7d67..9ba2c4d86076 100644
--- a/sys/compat/linuxkpi/common/include/linux/atomic.h
+++ b/sys/compat/linuxkpi/common/include/linux/atomic.h
@@ -29,7 +29,9 @@
#include <asm/atomic.h>
#include <asm/atomic64.h>
+#include <linux/atomic/atomic-arch-fallback.h>
#include <asm/atomic-long.h>
+#include <linux/atomic/atomic-instrumented.h>
#include <asm/barrier.h>
#endif /* _LINUXKPI_LINUX_ATOMIC_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/atomic/atomic-arch-fallback.h b/sys/compat/linuxkpi/common/include/linux/atomic/atomic-arch-fallback.h
new file mode 100644
index 000000000000..2980c364cb88
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/atomic/atomic-arch-fallback.h
@@ -0,0 +1,16 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 The FreeBSD Foundation
+ */
+
+#ifndef _LINUXKPI_LINUX_ATOMIC_ATOMIC_ARCH_FALLBACK_H_
+#define _LINUXKPI_LINUX_ATOMIC_ATOMIC_ARCH_FALLBACK_H_
+
+static inline int
+raw_atomic_read_acquire(const atomic_t *v)
+{
+ return (atomic_load_acq_int(&v->counter));
+}
+
+#endif /* _LINUXKPI_LINUX_ATOMIC_ATOMIC_ARCH_FALLBACK_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/atomic/atomic-instrumented.h b/sys/compat/linuxkpi/common/include/linux/atomic/atomic-instrumented.h
new file mode 100644
index 000000000000..af0b6af2ecfa
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/atomic/atomic-instrumented.h
@@ -0,0 +1,16 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 The FreeBSD Foundation
+ */
+
+#ifndef _LINUXKPI_LINUX_ATOMIC_ATOMIC_INSTRUMENTED_H_
+#define _LINUXKPI_LINUX_ATOMIC_ATOMIC_INSTRUMENTED_H_
+
+static inline int
+atomic_read_acquire(const atomic_t *v)
+{
+ return (raw_atomic_read_acquire(v));
+}
+
+#endif /* _LINUXKPI_LINUX_ATOMIC_ATOMIC_INSTRUMENTED_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/bcm47xx_nvram.h b/sys/compat/linuxkpi/common/include/linux/bcm47xx_nvram.h
new file mode 100644
index 000000000000..744101a2f8b1
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/bcm47xx_nvram.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2025 Bjoern A. Zeeb
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef _LINUXKPI_LINUX_BCM47XX_NVRAM_H
+#define _LINUXKPI_LINUX_BCM47XX_NVRAM_H
+
+static inline char *
+bcm47xx_nvram_get_contents(size_t *x __unused)
+{
+ return (NULL);
+};
+
+static inline void
+bcm47xx_nvram_release_contents(const char *x __unused)
+{
+};
+
+#endif /* _LINUXKPI_LINUX_BCM47XX_NVRAM_H */
diff --git a/sys/compat/linuxkpi/common/include/linux/bcma/bcma.h b/sys/compat/linuxkpi/common/include/linux/bcma/bcma.h
new file mode 100644
index 000000000000..3840c3a420e5
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/bcma/bcma.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2025 Bjoern A. Zeeb
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef _LINUXKPI_LINUX_BCMA_BCMA_H
+#define _LINUXKPI_LINUX_BCMA_BCMA_H
+
+#define BCMA_CORE_80211 0x812
+#define BCMA_CORE_ARM_CA7 0x847
+#define BCMA_CORE_ARM_CM3 0x82A
+#define BCMA_CORE_ARM_CR4 0x83E
+#define BCMA_CORE_CHIPCOMMON 0x800
+#define BCMA_CORE_GCI 0x840
+#define BCMA_CORE_INTERNAL_MEM 0x80E
+#define BCMA_CORE_PCIE2 0x83C
+#define BCMA_CORE_PMU 0x827
+#define BCMA_CORE_SDIO_DEV 0x829
+#define BCMA_CORE_SYS_MEM 0x849
+
+/* XXX not sure where these belong. */
+#define BCMA_CC_CAP_EXT_AOB_PRESENT 0x00000040
+#define BCMA_CC_PMU_CTL_RES_SHIFT 13
+#define BCMA_CC_PMU_CTL_RES_RELOAD 0x2
+#define BCMA_CC_SROM_CONTROL_OTPSEL 0x00000010
+#define BCMA_CC_SROM_CONTROL_OTP_PRESENT 0x00000020
+
+#endif /* _LINUXKPI_LINUX_BCMA_BCMA_H */
diff --git a/sys/compat/linuxkpi/common/include/linux/bcma/bcma_regs.h b/sys/compat/linuxkpi/common/include/linux/bcma/bcma_regs.h
new file mode 100644
index 000000000000..0a4cdddf7a73
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/bcma/bcma_regs.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2025 Bjoern A. Zeeb
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef _LINUXKPI_LINUX_BCMA_BCMA_REGS_H
+#define _LINUXKPI_LINUX_BCMA_BCMA_REGS_H
+
+#define BCMA_IOCTL 0x0408
+#define BCMA_IOCTL_CLK 0x0001
+#define BCMA_IOCTL_FGC 0x0002
+
+#define BCMA_RESET_CTL 0x0800
+#define BCMA_RESET_CTL_RESET 0x0001
+
+#endif /* _LINUXKPI_LINUX_BCMA_BCMA_REGS_H */
diff --git a/sys/compat/linuxkpi/common/include/linux/bitops.h b/sys/compat/linuxkpi/common/include/linux/bitops.h
index ebe9aa120094..0244c7d6f476 100644
--- a/sys/compat/linuxkpi/common/include/linux/bitops.h
+++ b/sys/compat/linuxkpi/common/include/linux/bitops.h
@@ -35,6 +35,8 @@
#include <sys/errno.h>
#include <sys/libkern.h>
+#include <linux/bits.h>
+
#define BIT(nr) (1UL << (nr))
#define BIT_ULL(nr) (1ULL << (nr))
#define BITS_PER_LONG (__SIZEOF_LONG__ * __CHAR_BIT__)
@@ -45,18 +47,10 @@
#define BITS_TO_LONGS(n) howmany((n), BITS_PER_LONG)
#define BIT_MASK(nr) (1UL << ((nr) & (BITS_PER_LONG - 1)))
#define BIT_WORD(nr) ((nr) / BITS_PER_LONG)
-#define GENMASK(h, l) (((~0UL) >> (BITS_PER_LONG - (h) - 1)) & ((~0UL) << (l)))
-#define GENMASK_ULL(h, l) (((~0ULL) >> (BITS_PER_LONG_LONG - (h) - 1)) & ((~0ULL) << (l)))
#define BITS_PER_BYTE 8
#define BITS_PER_TYPE(t) (sizeof(t) * BITS_PER_BYTE)
#define BITS_TO_BYTES(n) howmany((n), BITS_PER_BYTE)
-#define hweight8(x) bitcount((uint8_t)(x))
-#define hweight16(x) bitcount16(x)
-#define hweight32(x) bitcount32(x)
-#define hweight64(x) bitcount64(x)
-#define hweight_long(x) bitcountl(x)
-
#if __has_builtin(__builtin_popcountg)
#define HWEIGHT8(x) (__builtin_popcountg((uint8_t)(x)))
#define HWEIGHT16(x) (__builtin_popcountg((uint16_t)(x)))
@@ -70,6 +64,12 @@
#define HWEIGHT64(x) (__const_bitcount64((uint64_t)(x)))
#endif
+#define hweight8(x) (__builtin_constant_p(x) ? HWEIGHT8(x) : bitcount((uint8_t)(x)))
+#define hweight16(x) (__builtin_constant_p(x) ? HWEIGHT16(x) : bitcount16(x))
+#define hweight32(x) (__builtin_constant_p(x) ? HWEIGHT32(x) : bitcount32(x))
+#define hweight64(x) (__builtin_constant_p(x) ? HWEIGHT64(x) : bitcount64(x))
+#define hweight_long(x) bitcountl(x)
+
static inline int
__ffs(int mask)
{
diff --git a/sys/compat/linuxkpi/common/include/linux/bits.h b/sys/compat/linuxkpi/common/include/linux/bits.h
new file mode 100644
index 000000000000..53deb7cbbe88
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/bits.h
@@ -0,0 +1,14 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2016 Kip Macy
+ * Copyright (c) 2018 Johannes Lundberg
+ */
+
+#ifndef _LINUXKPI_LINUX_BITS_H_
+#define _LINUXKPI_LINUX_BITS_H_
+
+#define GENMASK(h, l) (((~0UL) >> (BITS_PER_LONG - (h) - 1)) & ((~0UL) << (l)))
+#define GENMASK_ULL(h, l) (((~0ULL) >> (BITS_PER_LONG_LONG - (h) - 1)) & ((~0ULL) << (l)))
+
+#endif /* _LINUXKPI_LINUX_BITS_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/cleanup.h b/sys/compat/linuxkpi/common/include/linux/cleanup.h
index 5bb146f082ed..fb21a81f121b 100644
--- a/sys/compat/linuxkpi/common/include/linux/cleanup.h
+++ b/sys/compat/linuxkpi/common/include/linux/cleanup.h
@@ -1,7 +1,7 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2024-2025 The FreeBSD Foundation
+ * Copyright (c) 2024-2026 The FreeBSD Foundation
*
* This software was developed by Björn Zeeb under sponsorship from
* the FreeBSD Foundation.
@@ -10,18 +10,26 @@
#ifndef _LINUXKPI_LINUX_CLEANUP_H
#define _LINUXKPI_LINUX_CLEANUP_H
+#include <linux/err.h>
+
+#define CLEANUP_NAME(_n, _s) __CONCAT(__CONCAT(cleanup_, _n), _s)
+
#define __cleanup(_f) __attribute__((__cleanup__(_f)))
+#define DECLARE(_n, _x) \
+ CLEANUP_NAME(_n, _t) _x __cleanup(CLEANUP_NAME(_n, _destroy)) = \
+ CLEANUP_NAME(_n, _create)
+
/*
* Note: "_T" are special as they are exposed into common code for
* statements. Extra care should be taken when changing the code.
*/
#define DEFINE_GUARD(_n, _dt, _lock, _unlock) \
\
- typedef _dt guard_ ## _n ## _t; \
+ typedef _dt CLEANUP_NAME(_n, _t); \
\
static inline _dt \
- guard_ ## _n ## _create( _dt _T) \
+ CLEANUP_NAME(_n, _create)( _dt _T) \
{ \
_dt c; \
\
@@ -30,7 +38,7 @@
} \
\
static inline void \
- guard_ ## _n ## _destroy(_dt *t) \
+ CLEANUP_NAME(_n, _destroy)(_dt *t) \
{ \
_dt _T; \
\
@@ -39,9 +47,10 @@
}
/* We need to keep these calls unique. */
+#define _guard(_n, _x) \
+ DECLARE(_n, _x)
#define guard(_n) \
- guard_ ## _n ## _t guard_ ## _n ## _ ## __COUNTER__ \
- __cleanup(guard_ ## _n ## _destroy) = guard_ ## _n ## _create
+ _guard(_n, guard_ ## _n ## _ ## __COUNTER__)
#define DEFINE_FREE(_n, _t, _f) \
static inline void \
@@ -56,38 +65,100 @@
#define __free(_n) __cleanup(__free_##_n)
/*
- * Given this is a _0 version it should likely be broken up into parts.
- * But we have no idead what a _1, _2, ... version would do different
- * until we see a call.
- * This is used for a not-real-type (rcu). We use a bool to "simulate"
- * the lock held. Also _T still special, may not always be used, so tag
- * with __unused (or better the LinuxKPI __maybe_unused).
+ * Our initial version go broken up. Some simplifications like using
+ * "bool" for the lock had to be changed to a more general type.
+ * _T is still special and, like other bits, may not always be used,
+ * so tag with __unused (or better the LinuxKPI __maybe_unused).
*/
-#define DEFINE_LOCK_GUARD_0(_n, _lock, _unlock, ...) \
+#define _DEFINE_LOCK_GUARD_0(_n, _lock) \
+ static inline CLEANUP_NAME(_n, _t) \
+ CLEANUP_NAME(_n, _create)(void) \
+ { \
+ CLEANUP_NAME(_n, _t) _tmp; \
+ CLEANUP_NAME(_n, _t) *_T __maybe_unused; \
+ \
+ _tmp.lock = (void *)1; \
+ _T = &_tmp; \
+ _lock; \
+ return (_tmp); \
+ }
+
+#define _DEFINE_LOCK_GUARD_1(_n, _type, _lock) \
+ static inline CLEANUP_NAME(_n, _t) \
+ CLEANUP_NAME(_n, _create)(_type *l) \
+ { \
+ CLEANUP_NAME(_n, _t) _tmp; \
+ CLEANUP_NAME(_n, _t) *_T __maybe_unused; \
\
+ _tmp.lock = l; \
+ _T = &_tmp; \
+ _lock; \
+ return (_tmp); \
+ }
+
+#define _GUARD_IS_ERR(_v) \
+ ({ \
+ uintptr_t x = (uintptr_t)(void *)(_v); \
+ IS_ERR_VALUE(x); \
+ })
+
+#define __is_cond_ptr(_n) \
+ CLEANUP_NAME(_n, _is_cond)
+#define __guard_ptr(_n) \
+ CLEANUP_NAME(_n, _ptr)
+
+#define _DEFINE_CLEANUP_IS_CONDITIONAL(_n, _b) \
+ static const bool CLEANUP_NAME(_n, _is_cond) __maybe_unused = _b
+
+#define _DEFINE_GUARD_LOCK_PTR(_n, _lp) \
+ static inline void * \
+ CLEANUP_NAME(_n, _lock_ptr)(CLEANUP_NAME(_n, _t) *_T) \
+ { \
+ void *_p; \
+ \
+ _p = (void *)(uintptr_t)*(_lp); \
+ if (IS_ERR(_p)) \
+ _p = NULL; \
+ return (_p); \
+ }
+
+#define _DEFINE_UNLOCK_GUARD(_n, _type, _unlock, ...) \
typedef struct { \
- bool lock; \
+ _type *lock; \
__VA_ARGS__; \
- } guard_ ## _n ## _t; \
+ } CLEANUP_NAME(_n, _t); \
\
static inline void \
- guard_ ## _n ## _destroy(guard_ ## _n ## _t *_T) \
+ CLEANUP_NAME(_n, _destroy)(CLEANUP_NAME(_n, _t) *_T) \
{ \
- if (_T->lock) { \
+ if (!_GUARD_IS_ERR(_T->lock)) { \
_unlock; \
} \
} \
\
- static inline guard_ ## _n ## _t \
- guard_ ## _n ## _create(void) \
- { \
- guard_ ## _n ## _t _tmp; \
- guard_ ## _n ## _t *_T __maybe_unused; \
- \
- _tmp.lock = true; \
- _T = &_tmp; \
- _lock; \
- return (_tmp); \
- }
+ _DEFINE_GUARD_LOCK_PTR(_n, &_T->lock)
+
+#define DEFINE_LOCK_GUARD_0(_n, _lock, _unlock, ...) \
+ _DEFINE_CLEANUP_IS_CONDITIONAL(_n, false); \
+ _DEFINE_UNLOCK_GUARD(_n, void, _unlock, __VA_ARGS__) \
+ _DEFINE_LOCK_GUARD_0(_n, _lock)
+
+/* This allows the type to be set. */
+#define DEFINE_LOCK_GUARD_1(_n, _t, _lock, _unlock, ...) \
+ _DEFINE_CLEANUP_IS_CONDITIONAL(_n, false); \
+ _DEFINE_UNLOCK_GUARD(_n, _t, _unlock, __VA_ARGS__) \
+ _DEFINE_LOCK_GUARD_1(_n, _t, _lock)
+
+#define _scoped_guard(_n, _l, ...) \
+ for (DECLARE(_n, _scoped)(__VA_ARGS__); \
+ 1 /*__guard_ptr(_n)(&_scoped) || !__is_cond_ptr(_n) */; \
+ ({ goto _l; })) \
+ if (0) { \
+_l: \
+ break; \
+ } else
+
+#define scoped_guard(_n, ...) \
+ _scoped_guard(_n, ___label_ ## __COUNTER__, ##__VA_ARGS__)
#endif /* _LINUXKPI_LINUX_CLEANUP_H */
diff --git a/sys/compat/linuxkpi/common/include/linux/compiler.h b/sys/compat/linuxkpi/common/include/linux/compiler.h
index 90d907dd4d45..4fcd1f9f342b 100644
--- a/sys/compat/linuxkpi/common/include/linux/compiler.h
+++ b/sys/compat/linuxkpi/common/include/linux/compiler.h
@@ -66,9 +66,6 @@
#define barrier() __asm__ __volatile__("": : :"memory")
-#define lower_32_bits(n) ((u32)(n))
-#define upper_32_bits(n) ((u32)(((n) >> 16) >> 16))
-
#define WRITE_ONCE(x,v) do { \
barrier(); \
(*(volatile __typeof(x) *)(uintptr_t)&(x)) = (v); \
diff --git a/sys/compat/linuxkpi/common/include/linux/compiler_types.h b/sys/compat/linuxkpi/common/include/linux/compiler_types.h
index 7151c03de690..25e69f5afd8e 100644
--- a/sys/compat/linuxkpi/common/include/linux/compiler_types.h
+++ b/sys/compat/linuxkpi/common/include/linux/compiler_types.h
@@ -42,4 +42,17 @@
#define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
+/*
+ * __builtin_counted_by_ref was introduced to
+ * - gcc in e7380688fa59 with a first release tag of gcc-15.1.0,
+ * - llvm in 7475156d49406 with a first release tag of llvmorg-20.1.0-rc1
+ * but cannot be used before 23 (22.x.y possibly) (see 09a3d830a888).
+ */
+#if (__has_builtin(__builtin_counted_by_ref)) && \
+ (!defined(__clang__) || (__clang_major__ >= 23))
+#define __flex_counter(_field) __builtin_counted_by_ref(_field)
+#else
+#define __flex_counter(_field) ((void *)NULL)
+#endif
+
#endif
diff --git a/sys/compat/linuxkpi/common/include/linux/device.h b/sys/compat/linuxkpi/common/include/linux/device.h
index 8834f1799e61..2913810923f1 100644
--- a/sys/compat/linuxkpi/common/include/linux/device.h
+++ b/sys/compat/linuxkpi/common/include/linux/device.h
@@ -267,6 +267,30 @@ show_class_attr_string(struct class *class,
dev_dbg(dev, __VA_ARGS__); \
} while (0)
+static inline int
+dev_err_probe(const struct device *dev, int err, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+
+ /*
+ * On Linux, they look at the error code to determine if the message
+ * should be logged (not logged if -ENOMEM) and at which log level.
+ */
+ device_printf(dev->bsddev, fmt, args);
+
+ va_end(args);
+
+ return (err);
+}
+
+#define dev_err_ptr_probe(dev, err, fmt, ...) \
+ ERR_PTR(dev_err_probe((dev), (err), fmt, ##__VA_ARGS__)
+
+#define dev_err_cast_probe(dev, err, fmt, ...) \
+ ERR_PTR(dev_err_probe((dev), PTR_ERR(err), fmt, ##__VA_ARGS__)
+
/* Public and LinuxKPI internal devres functions. */
void *lkpi_devres_alloc(void(*release)(struct device *, void *), size_t, gfp_t);
void lkpi_devres_add(struct device *, void *);
@@ -701,6 +725,13 @@ devm_kmemdup(struct device *dev, const void *src, size_t len, gfp_t gfp)
return (dst);
}
+static inline void *
+devm_kmemdup_array(struct device *dev, const void *src, size_t n, size_t len,
+ gfp_t gfp)
+{
+ return (devm_kmemdup(dev, src, size_mul(n, len), gfp));
+}
+
#define devm_kzalloc(_dev, _size, _gfp) \
devm_kmalloc((_dev), (_size), (_gfp) | __GFP_ZERO)
diff --git a/sys/compat/linuxkpi/common/include/linux/dma-mapping.h b/sys/compat/linuxkpi/common/include/linux/dma-mapping.h
index 2d8e1196d3d3..5e5d40ef8339 100644
--- a/sys/compat/linuxkpi/common/include/linux/dma-mapping.h
+++ b/sys/compat/linuxkpi/common/include/linux/dma-mapping.h
@@ -96,6 +96,8 @@ void *linux_dma_alloc_coherent(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t flag);
void *linuxkpi_dmam_alloc_coherent(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t flag);
+void linuxkpi_dmam_free_coherent(struct device *dev, size_t size,
+ void *addr, dma_addr_t dma_handle);
dma_addr_t linux_dma_map_phys(struct device *dev, vm_paddr_t phys, size_t len); /* backward compat */
dma_addr_t lkpi_dma_map_phys(struct device *, vm_paddr_t, size_t,
enum dma_data_direction, unsigned long);
@@ -104,10 +106,10 @@ void lkpi_dma_unmap(struct device *, dma_addr_t, size_t,
enum dma_data_direction, unsigned long);
int linux_dma_map_sg_attrs(struct device *dev, struct scatterlist *sgl,
int nents, enum dma_data_direction direction,
- unsigned long attrs __unused);
+ unsigned long attrs);
void linux_dma_unmap_sg_attrs(struct device *dev, struct scatterlist *sg,
int nents __unused, enum dma_data_direction direction,
- unsigned long attrs __unused);
+ unsigned long attrs);
void linuxkpi_dma_sync(struct device *, dma_addr_t, size_t, bus_dmasync_op_t);
static inline int
@@ -181,6 +183,13 @@ dma_free_coherent(struct device *dev, size_t size, void *cpu_addr,
kmem_free(cpu_addr, size);
}
+static inline void
+dmam_free_coherent(struct device *dev, size_t size, void *addr,
+ dma_addr_t dma_handle)
+{
+ linuxkpi_dmam_free_coherent(dev, size, addr, dma_handle);
+}
+
static inline dma_addr_t
dma_map_page_attrs(struct device *dev, struct page *page, size_t offset,
size_t size, enum dma_data_direction direction, unsigned long attrs)
@@ -192,10 +201,10 @@ dma_map_page_attrs(struct device *dev, struct page *page, size_t offset,
/* linux_dma_(un)map_sg_attrs does not support attrs yet */
#define dma_map_sg_attrs(dev, sgl, nents, dir, attrs) \
- linux_dma_map_sg_attrs(dev, sgl, nents, dir, 0)
+ linux_dma_map_sg_attrs(dev, sgl, nents, dir, attrs)
#define dma_unmap_sg_attrs(dev, sg, nents, dir, attrs) \
- linux_dma_unmap_sg_attrs(dev, sg, nents, dir, 0)
+ linux_dma_unmap_sg_attrs(dev, sg, nents, dir, attrs)
static inline dma_addr_t
dma_map_page(struct device *dev, struct page *page,
@@ -352,10 +361,10 @@ dma_max_mapping_size(struct device *dev)
}
#define dma_map_single_attrs(dev, ptr, size, dir, attrs) \
- _dma_map_single_attrs(dev, ptr, size, dir, 0)
+ _dma_map_single_attrs(dev, ptr, size, dir, attrs)
#define dma_unmap_single_attrs(dev, dma_addr, size, dir, attrs) \
- _dma_unmap_single_attrs(dev, dma_addr, size, dir, 0)
+ _dma_unmap_single_attrs(dev, dma_addr, size, dir, attrs)
#define dma_map_single(d, a, s, r) dma_map_single_attrs(d, a, s, r, 0)
#define dma_unmap_single(d, a, s, r) dma_unmap_single_attrs(d, a, s, r, 0)
diff --git a/sys/compat/linuxkpi/common/include/linux/eventfd.h b/sys/compat/linuxkpi/common/include/linux/eventfd.h
new file mode 100644
index 000000000000..d167d4b7d189
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/eventfd.h
@@ -0,0 +1,54 @@
+/*-
+ * Copyright (c) 2025 The FreeBSD Foundation
+ * Copyright (c) 2025 Jean-Sébastien Pédron
+ *
+ * This software was developed by Jean-Sébastien Pédron under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * 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.
+ */
+
+#ifndef _LINUXKPI_LINUX_EVENTFD_H_
+#define _LINUXKPI_LINUX_EVENTFD_H_
+
+#include <sys/eventfd.h>
+
+#include <linux/wait.h>
+#include <linux/err.h>
+#include <linux/percpu-defs.h>
+#include <linux/percpu.h>
+#include <linux/sched.h>
+
+/*
+ * Linux uses `struct eventfd_ctx`, but FreeBSD defines `struct eventfd`. Here,
+ * we define a synonym to the FreeBSD structure. This allows to keep Linux code
+ * unmodified.
+ */
+#define eventfd_ctx eventfd
+
+#define eventfd_ctx_fdget lkpi_eventfd_ctx_fdget
+struct eventfd_ctx *lkpi_eventfd_ctx_fdget(int fd);
+
+#define eventfd_ctx_put lkpi_eventfd_ctx_put
+void lkpi_eventfd_ctx_put(struct eventfd_ctx *ctx);
+
+#endif /* _LINUXKPI_LINUX_EVENTFD_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/file.h b/sys/compat/linuxkpi/common/include/linux/file.h
index be12d5f1bccf..4d8f8fba7fab 100644
--- a/sys/compat/linuxkpi/common/include/linux/file.h
+++ b/sys/compat/linuxkpi/common/include/linux/file.h
@@ -184,6 +184,8 @@ static inline struct fd fdget(unsigned int fd)
return (struct fd){f};
}
+#define fd_file(fd) ((fd).linux_file)
+
#define file linux_file
#define fget(...) linux_fget(__VA_ARGS__)
diff --git a/sys/compat/linuxkpi/common/include/linux/fs.h b/sys/compat/linuxkpi/common/include/linux/fs.h
index f1568ad6282d..749a9fd22d3d 100644
--- a/sys/compat/linuxkpi/common/include/linux/fs.h
+++ b/sys/compat/linuxkpi/common/include/linux/fs.h
@@ -133,8 +133,11 @@ do { \
typedef int (*filldir_t)(void *, const char *, int, off_t, u64, unsigned);
+typedef unsigned int fop_flags_t;
+
struct file_operations {
struct module *owner;
+ fop_flags_t fop_flags; /* Unused on FreeBSD. */
ssize_t (*read)(struct linux_file *, char __user *, size_t, off_t *);
ssize_t (*write)(struct linux_file *, const char __user *, size_t, off_t *);
unsigned int (*poll) (struct linux_file *, struct poll_table_struct *);
@@ -182,6 +185,14 @@ struct file_operations {
int (*setlease)(struct file *, long, struct file_lock **);
#endif
};
+
+#define FOP_BUFFER_RASYNC (1 << 0)
+#define FOP_BUFFER_WASYNC (1 << 1)
+#define FOP_MMAP_SYNC (1 << 2)
+#define FOP_DIO_PARALLEL_WRITE (1 << 3)
+#define FOP_HUGE_PAGES (1 << 4)
+#define FOP_UNSIGNED_OFFSET (1 << 5)
+
#define fops_get(fops) (fops)
#define replace_fops(f, fops) ((f)->f_op = (fops))
@@ -364,27 +375,23 @@ static inline ssize_t
simple_read_from_buffer(void __user *dest, size_t read_size, loff_t *ppos,
void *orig, size_t buf_size)
{
- void *p, *read_pos = ((char *) orig) + *ppos;
+ void *read_pos = ((char *) orig) + *ppos;
size_t buf_remain = buf_size - *ppos;
+ ssize_t num_read;
- if (buf_remain < 0 || buf_remain > buf_size)
- return -EINVAL;
+ if (*ppos >= buf_size || read_size == 0)
+ return (0);
if (read_size > buf_remain)
read_size = buf_remain;
- /*
- * XXX At time of commit only debugfs consumers could be
- * identified. If others will use this function we may
- * have to revise this: normally we would call copy_to_user()
- * here but lindebugfs will return the result and the
- * copyout is done elsewhere for us.
- */
- p = memcpy(dest, read_pos, read_size);
- if (p != NULL)
- *ppos += read_size;
-
- return (read_size);
+ /* copy_to_user returns number of bytes NOT read */
+ num_read = read_size - copy_to_user(dest, read_pos, read_size);
+ if (num_read == 0)
+ return -EFAULT;
+ *ppos += num_read;
+
+ return (num_read);
}
MALLOC_DECLARE(M_LSATTR);
@@ -415,11 +422,13 @@ int simple_attr_open(struct inode *inode, struct file *filp,
int simple_attr_release(struct inode *inode, struct file *filp);
-ssize_t simple_attr_read(struct file *filp, char *buf, size_t read_size, loff_t *ppos);
+ssize_t simple_attr_read(struct file *filp, char __user *buf, size_t read_size,
+ loff_t *ppos);
-ssize_t simple_attr_write(struct file *filp, const char *buf, size_t write_size, loff_t *ppos);
+ssize_t simple_attr_write(struct file *filp, const char __user *buf,
+ size_t write_size, loff_t *ppos);
-ssize_t simple_attr_write_signed(struct file *filp, const char *buf,
+ssize_t simple_attr_write_signed(struct file *filp, const char __user *buf,
size_t write_size, loff_t *ppos);
#endif /* _LINUXKPI_LINUX_FS_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/gfp.h b/sys/compat/linuxkpi/common/include/linux/gfp.h
index 7a32e7862338..5c638be92beb 100644
--- a/sys/compat/linuxkpi/common/include/linux/gfp.h
+++ b/sys/compat/linuxkpi/common/include/linux/gfp.h
@@ -55,16 +55,19 @@
#define __GFP_IO 0
#define __GFP_NO_KSWAPD 0
#define __GFP_KSWAPD_RECLAIM 0
+#define __GFP_ACCOUNT 0
#define __GFP_WAIT M_WAITOK
#define __GFP_DMA32 (1U << 24) /* LinuxKPI only */
#define __GFP_NORETRY (1U << 25) /* LinuxKPI only */
-#define __GFP_BITS_SHIFT 26
+#define __GFP_THISNODE (1U << 26)
+#define __GFP_BITS_SHIFT 27
#define __GFP_BITS_MASK ((1 << __GFP_BITS_SHIFT) - 1)
#define __GFP_NOFAIL M_WAITOK
#define GFP_NOWAIT M_NOWAIT
#define GFP_ATOMIC (M_NOWAIT | M_USE_RESERVE)
#define GFP_KERNEL M_WAITOK
+#define GFP_KERNEL_ACCOUNT (GFP_KERNEL | __GFP_ACCOUNT)
#define GFP_USER M_WAITOK
#define GFP_HIGHUSER M_WAITOK
#define GFP_HIGHUSER_MOVABLE M_WAITOK
@@ -80,6 +83,9 @@
CTASSERT((__GFP_DMA32 & GFP_NATIVE_MASK) == 0);
CTASSERT((__GFP_BITS_MASK & GFP_NATIVE_MASK) == GFP_NATIVE_MASK);
+#define __default_gfp(_discard, _arg_or_default, ...) _arg_or_default
+#define default_gfp(...) __default_gfp(, ##__VA_ARGS__, GFP_KERNEL)
+
struct page_frag_cache {
void *va;
int pagecnt_bias;
diff --git a/sys/compat/linuxkpi/common/include/linux/hardirq.h b/sys/compat/linuxkpi/common/include/linux/hardirq.h
index f79451dd0d35..c6cbf1a34f14 100644
--- a/sys/compat/linuxkpi/common/include/linux/hardirq.h
+++ b/sys/compat/linuxkpi/common/include/linux/hardirq.h
@@ -31,6 +31,7 @@
#include <linux/types.h>
#include <linux/lockdep.h>
+#include <linux/preempt.h>
#include <sys/param.h>
#include <sys/bus.h>
diff --git a/sys/compat/linuxkpi/common/include/linux/hex.h b/sys/compat/linuxkpi/common/include/linux/hex.h
new file mode 100644
index 000000000000..7e9f499cfb1b
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/hex.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2022-2026 Bjoern A. Zeeb
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef _LINUXKPI_LINUX_HEX_H_
+#define _LINUXKPI_LINUX_HEX_H_
+
+#include <linux/types.h>
+#include <linux/errno.h>
+
+static inline int
+_h2b(const char c)
+{
+
+ if (c >= '0' && c <= '9')
+ return (c - '0');
+ if (c >= 'a' && c <= 'f')
+ return (10 + c - 'a');
+ if (c >= 'A' && c <= 'F')
+ return (10 + c - 'A');
+ return (-EINVAL);
+}
+
+static inline int
+hex2bin(uint8_t *bindst, const char *hexsrc, size_t binlen)
+{
+ int hi4, lo4;
+
+ while (binlen > 0) {
+ hi4 = _h2b(*hexsrc++);
+ lo4 = _h2b(*hexsrc++);
+ if (hi4 < 0 || lo4 < 0)
+ return (-EINVAL);
+
+ *bindst++ = (hi4 << 4) | lo4;
+ binlen--;
+ }
+
+ return (0);
+}
+
+#endif /* _LINUXKPI_LINUX_HEX_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/highmem.h b/sys/compat/linuxkpi/common/include/linux/highmem.h
index 58a9cdcdf60f..b704e77b6c2f 100644
--- a/sys/compat/linuxkpi/common/include/linux/highmem.h
+++ b/sys/compat/linuxkpi/common/include/linux/highmem.h
@@ -45,6 +45,7 @@
#include <linux/mm.h>
#include <linux/page.h>
+#include <linux/hardirq.h>
#define PageHighMem(p) (0)
@@ -61,7 +62,7 @@ kmap(struct page *page)
struct sf_buf *sf;
if (PMAP_HAS_DMAP) {
- return ((void *)PHYS_TO_DMAP(page_to_phys(page)));
+ return (PHYS_TO_DMAP(page_to_phys(page)));
} else {
sched_pin();
sf = sf_buf_alloc(page, SFB_NOWAIT | SFB_CPUPRIVATE);
@@ -69,7 +70,7 @@ kmap(struct page *page)
sched_unpin();
return (NULL);
}
- return ((void *)sf_buf_kva(sf));
+ return (sf_buf_kva(sf));
}
}
@@ -99,6 +100,19 @@ kmap_local_page(struct page *page)
}
static inline void *
+kmap_local_folio(struct folio *folio, size_t offset)
+{
+ struct page *page;
+ char *vaddr;
+
+ page = &folio->page;
+ vaddr = kmap_local_page(page);
+ vaddr += offset;
+
+ return (vaddr);
+}
+
+static inline void *
kmap_local_page_prot(struct page *page, pgprot_t prot)
{
@@ -167,4 +181,13 @@ memcpy_to_page(struct page *page, size_t offset, const char *from, size_t len)
kunmap_local(to);
}
+static inline void
+memcpy_to_folio(struct folio *folio, size_t offset, const char *from, size_t len)
+{
+ struct page *page;
+
+ page = &folio->page;
+ memcpy_to_page(page, offset, from, len);
+}
+
#endif /* _LINUXKPI_LINUX_HIGHMEM_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/ieee80211.h b/sys/compat/linuxkpi/common/include/linux/ieee80211.h
index b5051a9f7d21..b8f29560f200 100644
--- a/sys/compat/linuxkpi/common/include/linux/ieee80211.h
+++ b/sys/compat/linuxkpi/common/include/linux/ieee80211.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2020-2025 The FreeBSD Foundation
+ * Copyright (c) 2020-2026 The FreeBSD Foundation
*
* This software was developed by Björn Zeeb under sponsorship from
* the FreeBSD Foundation.
@@ -51,8 +51,16 @@ extern int linuxkpi_debug_80211;
#define IMPROVE(fmt, ...) if (linuxkpi_debug_80211 & D80211_IMPROVE) \
printf("%s:%d: XXX LKPI80211 IMPROVE " fmt "\n", __func__, __LINE__, ##__VA_ARGS__)
-
-/* 9.4.2.55 Management MIC element (CMAC-256, GMAC-128, and GMAC-256). */
+/* 802.11-2024, 9.4.2.53 MME. */
+/* BIP-CMAC-128 */
+struct ieee80211_mmie {
+ uint8_t element_id;
+ uint8_t length;
+ uint16_t key_id;
+ uint8_t ipn[6];
+ uint8_t mic[8];
+};
+/* BIP-CMAC-256, BIP-GMAC-128, BIP-GMAC-256 */
struct ieee80211_mmie_16 {
uint8_t element_id;
uint8_t length;
@@ -92,8 +100,6 @@ struct ieee80211_mmie_16 {
#define IEEE80211_MAX_RTS_THRESHOLD 2346 /* net80211::IEEE80211_RTS_MAX */
-#define IEEE80211_MIN_ACTION_SIZE 23 /* ? */
-
/* Wi-Fi Peer-to-Peer (P2P) Technical Specification */
#define IEEE80211_P2P_OPPPS_CTWINDOW_MASK 0x7f
#define IEEE80211_P2P_OPPPS_ENABLE_BIT BIT(7)
@@ -211,6 +217,7 @@ enum ieee80211_min_mpdu_start_spacing {
#define IEEE80211_FCTL_TODS (IEEE80211_FC1_DIR_TODS << 8)
#define IEEE80211_FCTL_MOREFRAGS (IEEE80211_FC1_MORE_FRAG << 8)
#define IEEE80211_FCTL_PM (IEEE80211_FC1_PWR_MGT << 8)
+#define IEEE80211_FCTL_MOREDATA (IEEE80211_FC1_MORE_DATA << 8)
#define IEEE80211_FTYPE_MGMT IEEE80211_FC0_TYPE_MGT
#define IEEE80211_FTYPE_CTL IEEE80211_FC0_TYPE_CTL
@@ -472,18 +479,6 @@ enum ieee80211_tx_control_flags {
IEEE80211_TX_CTRL_MLO_LINK = 0xF0000000, /* This is IEEE80211_LINK_UNSPECIFIED on the high bits. */
};
-enum ieee80211_tx_rate_flags {
- /* XXX TODO .. right shift numbers */
- IEEE80211_TX_RC_40_MHZ_WIDTH = BIT(0),
- IEEE80211_TX_RC_80_MHZ_WIDTH = BIT(1),
- IEEE80211_TX_RC_160_MHZ_WIDTH = BIT(2),
- IEEE80211_TX_RC_GREEN_FIELD = BIT(3),
- IEEE80211_TX_RC_MCS = BIT(4),
- IEEE80211_TX_RC_SHORT_GI = BIT(5),
- IEEE80211_TX_RC_VHT_MCS = BIT(6),
- IEEE80211_TX_RC_USE_SHORT_PREAMBLE = BIT(7),
-};
-
#define IEEE80211_RNR_TBTT_PARAMS_PSD_RESERVED -128
#define IEEE80211_HT_CTL_LEN 4
@@ -625,6 +620,8 @@ struct ieee80211_mgmt {
} u;
} __packed __aligned(2);
+#define IEEE80211_MIN_ACTION_SIZE offsetof(struct ieee80211_mgmt, u.action.u)
+
struct ieee80211_cts { /* net80211::ieee80211_frame_cts */
__le16 frame_control;
__le16 duration;
diff --git a/sys/compat/linuxkpi/common/include/linux/instruction_pointer.h b/sys/compat/linuxkpi/common/include/linux/instruction_pointer.h
new file mode 100644
index 000000000000..200a8c3f1d5e
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/instruction_pointer.h
@@ -0,0 +1,15 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2021 Neel Chauhan
+ * Copyright (c) 2026 The FreeBSD Foundation
+ */
+
+#ifndef _LINUXKPI_LINUX_INSTRUCTION_POINTER_H_
+#define _LINUXKPI_LINUX_INSTRUCTION_POINTER_H_
+
+#define _RET_IP_ __builtin_return_address(0)
+
+#define _THIS_IP_ ((unsigned long)0) /* TODO: _THIS_IP_ not implemented. */
+
+#endif /* _LINUXKPI_LINUX_INSTRUCTION_POINTER_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/io.h b/sys/compat/linuxkpi/common/include/linux/io.h
index 2d6fef4e7c52..f89c150e7a3b 100644
--- a/sys/compat/linuxkpi/common/include/linux/io.h
+++ b/sys/compat/linuxkpi/common/include/linux/io.h
@@ -542,7 +542,7 @@ static inline int
arch_io_reserve_memtype_wc(resource_size_t start, resource_size_t size)
{
#if defined(__amd64__)
- vm_offset_t va;
+ void *va;
va = PHYS_TO_DMAP(start);
return (-pmap_change_attr(va, size, VM_MEMATTR_WRITE_COMBINING));
@@ -555,7 +555,7 @@ static inline void
arch_io_free_memtype_wc(resource_size_t start, resource_size_t size)
{
#if defined(__amd64__)
- vm_offset_t va;
+ void *va;
va = PHYS_TO_DMAP(start);
diff --git a/sys/compat/linuxkpi/common/include/linux/ioport.h b/sys/compat/linuxkpi/common/include/linux/ioport.h
index 763af2de7c4f..68f28cec4ec4 100644
--- a/sys/compat/linuxkpi/common/include/linux/ioport.h
+++ b/sys/compat/linuxkpi/common/include/linux/ioport.h
@@ -41,6 +41,7 @@ struct resource {
resource_size_t start;
resource_size_t end;
const char *name;
+ unsigned long flags;
};
static inline resource_size_t
diff --git a/sys/compat/linuxkpi/common/include/linux/kconfig.h b/sys/compat/linuxkpi/common/include/linux/kconfig.h
index c1d186b56e1f..529d41991900 100644
--- a/sys/compat/linuxkpi/common/include/linux/kconfig.h
+++ b/sys/compat/linuxkpi/common/include/linux/kconfig.h
@@ -73,4 +73,39 @@
#define IS_REACHABLE(_x) (IS_BUILTIN(_x) || \
(IS_MODULE(_x) && IS_BUILTIN(MODULE)))
+/*
+ * On Linux, the CONFIG_PGTABLE_LEVELS value is defined by the config/build
+ * system. Here, we take the per-architecture default value defined in
+ * `arch/$ARCH/Kconfig` files upstream.
+ */
+#if defined(__amd64__)
+
+#define CONFIG_PGTABLE_LEVELS 4
+
+#elif defined(__aarch64__)
+
+#define CONFIG_PGTABLE_LEVELS 4
+
+#elif defined(__i386__)
+
+#define CONFIG_PGTABLE_LEVELS 2
+
+#elif defined(__powerpc__)
+
+#if defined(__powerpc64__)
+#define CONFIG_PGTABLE_LEVELS 4
+#else
+#define CONFIG_PGTABLE_LEVELS 2
+#endif
+
+#elif defined(__riscv)
+
+#if defined(__riscv_xlen) && __riscv_xlen == 64
+#define CONFIG_PGTABLE_LEVELS 5
+#else
+#define CONFIG_PGTABLE_LEVELS 2
+#endif
+
+#endif
+
#endif /* _LINUXKPI_LINUX_KCONFIG_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/kdev_t.h b/sys/compat/linuxkpi/common/include/linux/kdev_t.h
index 988dd771254a..964b7078e7f5 100644
--- a/sys/compat/linuxkpi/common/include/linux/kdev_t.h
+++ b/sys/compat/linuxkpi/common/include/linux/kdev_t.h
@@ -31,6 +31,21 @@
#include <sys/types.h>
+/*
+ * The encoding of major/minor on FreeBSD is complex to take into account the
+ * compatibility with 16-bit dev_t (see <sys/types.h>). It is not possible to
+ * use a simple shift and thus, we can't have a proper definition of
+ * `MINORBITS`.
+ *
+ * At the time this is added to linuxkpi, this is used by the DRM generic code
+ * as the upper limit to allocate an ID using an xarray. It is not used to
+ * decode a minor, therefore any arbitrary value here is fine in this context.
+ *
+ * On Linux, the value is set to 20. Because of the above, we reuse this value,
+ * even so it does not correspond to the actual minor encoding on FreeBSD.
+ */
+#define MINORBITS 20
+
#define MAJOR(dev) major(dev)
#define MINOR(dev) minor(dev)
#define MKDEV(ma, mi) makedev(ma, mi)
diff --git a/sys/compat/linuxkpi/common/include/linux/kernel.h b/sys/compat/linuxkpi/common/include/linux/kernel.h
index 11a13cbd49b4..afbd9208a410 100644
--- a/sys/compat/linuxkpi/common/include/linux/kernel.h
+++ b/sys/compat/linuxkpi/common/include/linux/kernel.h
@@ -40,6 +40,7 @@
#include <sys/syslog.h>
#include <sys/time.h>
+#include <linux/array_size.h>
#include <linux/bitops.h>
#include <linux/build_bug.h>
#include <linux/compiler.h>
@@ -55,6 +56,9 @@
#include <linux/jiffies.h>
#include <linux/log2.h>
#include <linux/kconfig.h>
+#include <linux/instruction_pointer.h>
+#include <linux/hex.h>
+#include <linux/wordpart.h>
#include <asm/byteorder.h>
#include <asm/cpufeature.h>
@@ -263,12 +267,8 @@ extern int linuxkpi_debug;
})
#endif
-#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
-
#define u64_to_user_ptr(val) ((void *)(uintptr_t)(val))
-#define _RET_IP_ __builtin_return_address(0)
-
#define offsetofend(t, m) \
(offsetof(t, m) + sizeof((((t *)0)->m)))
@@ -306,37 +306,6 @@ linux_ratelimited(linux_ratelimit_t *rl)
#define add_taint(x,y) do { \
} while (0)
-static inline int
-_h2b(const char c)
-{
-
- if (c >= '0' && c <= '9')
- return (c - '0');
- if (c >= 'a' && c <= 'f')
- return (10 + c - 'a');
- if (c >= 'A' && c <= 'F')
- return (10 + c - 'A');
- return (-EINVAL);
-}
-
-static inline int
-hex2bin(uint8_t *bindst, const char *hexsrc, size_t binlen)
-{
- int hi4, lo4;
-
- while (binlen > 0) {
- hi4 = _h2b(*hexsrc++);
- lo4 = _h2b(*hexsrc++);
- if (hi4 < 0 || lo4 < 0)
- return (-EINVAL);
-
- *bindst++ = (hi4 << 4) | lo4;
- binlen--;
- }
-
- return (0);
-}
-
static inline bool
mac_pton(const char *macin, uint8_t *macout)
{
@@ -382,4 +351,48 @@ mac_pton(const char *macin, uint8_t *macout)
#define DECLARE_FLEX_ARRAY(_t, _n) \
struct { struct { } __dummy_ ## _n; _t _n[0]; }
+/*
+ * The following functions/macros are debug/diagnostics tools. They default to
+ * no-ops, except `might_sleep()` which uses `WITNESS_WARN()` on FreeBSD.
+ */
+static inline void
+__might_resched(const char *file, int line, unsigned int offsets)
+{
+}
+
+static inline void
+__might_sleep(const char *file, int line)
+{
+}
+
+static inline void
+might_fault(void)
+{
+}
+
+#define might_sleep() \
+ WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "might_sleep()")
+
+#define might_sleep_if(cond) do { \
+ if (cond) { might_sleep(); } \
+} while (0)
+
+#define might_resched() do { } while (0)
+#define cant_sleep() do { } while (0)
+#define cant_migrate() do { } while (0)
+#define sched_annotate_sleep() do { } while (0)
+#define non_block_start() do { } while (0)
+#define non_block_end() do { } while (0)
+
+extern enum system_states {
+ SYSTEM_BOOTING,
+ SYSTEM_SCHEDULING,
+ SYSTEM_FREEING_INITMEM,
+ SYSTEM_RUNNING,
+ SYSTEM_HALT,
+ SYSTEM_POWER_OFF,
+ SYSTEM_RESTART,
+ SYSTEM_SUSPEND,
+} system_state;
+
#endif /* _LINUXKPI_LINUX_KERNEL_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/kfifo.h b/sys/compat/linuxkpi/common/include/linux/kfifo.h
index fbe16e22683e..c8cea1226b3c 100644
--- a/sys/compat/linuxkpi/common/include/linux/kfifo.h
+++ b/sys/compat/linuxkpi/common/include/linux/kfifo.h
@@ -30,6 +30,7 @@
#include <sys/types.h>
+#include <linux/array_size.h>
#include <linux/slab.h>
#include <linux/gfp.h>
@@ -89,7 +90,7 @@
(_kf)->head[(_kf)->last] = (_e); \
(_kf)->count++; \
(_kf)->last++; \
- if ((_kf)->last > (_kf)->total) \
+ if ((_kf)->last >= (_kf)->total) \
(_kf)->last = 0; \
_rc = true; \
} \
@@ -107,7 +108,7 @@
*(_e) = (_kf)->head[(_kf)->first]; \
(_kf)->count--; \
(_kf)->first++; \
- if ((_kf)->first > (_kf)->total) \
+ if ((_kf)->first >= (_kf)->total) \
(_kf)->first = 0; \
_rc = true; \
} \
diff --git a/sys/compat/linuxkpi/common/include/linux/kmsg_dump.h b/sys/compat/linuxkpi/common/include/linux/kmsg_dump.h
index 25f96b304f59..e7f95ced3047 100644
--- a/sys/compat/linuxkpi/common/include/linux/kmsg_dump.h
+++ b/sys/compat/linuxkpi/common/include/linux/kmsg_dump.h
@@ -25,13 +25,57 @@ enum kmsg_dump_reason {
KMSG_DUMP_MAX
};
+struct kmsg_dump_iter {
+ uint64_t cur_seq;
+ uint64_t next_seq;
+};
+
+struct kmsg_dump_detail {
+ enum kmsg_dump_reason reason;
+ const char *description;
+};
+
struct kmsg_dumper {
struct list_head list;
+#if defined(LINUXKPI_VERSION) && LINUXKPI_VERSION < 61200
void (*dump)(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason);
+#else
+ void (*dump)(struct kmsg_dumper *dumper, struct kmsg_dump_detail *detail);
+#endif
enum kmsg_dump_reason max_reason;
bool registered;
};
+static inline void
+kmsg_dump(enum kmsg_dump_reason reason)
+{
+ pr_debug("TODO");
+}
+
+static inline bool
+kmsg_dump_get_line(struct kmsg_dump_iter *iter, bool syslog,
+ const char *line, size_t size, size_t *len)
+{
+ pr_debug("TODO");
+
+ return (false);
+}
+
+static inline bool
+kmsg_dump_get_buffer(struct kmsg_dump_iter *iter, bool syslog,
+ char *buf, size_t size, size_t *len)
+{
+ pr_debug("TODO");
+
+ return (false);
+}
+
+static inline void
+kmsg_dump_rewind(struct kmsg_dump_iter *iter)
+{
+ pr_debug("TODO");
+}
+
static inline int
kmsg_dump_register(struct kmsg_dumper *dumper)
{
@@ -48,4 +92,11 @@ kmsg_dump_unregister(struct kmsg_dumper *dumper)
return (-EINVAL);
}
+static inline const char *
+kmsg_dump_reason_str(enum kmsg_dump_reason reason)
+{
+ pr_debug("TODO");
+
+ return ("Unknown");
+}
#endif
diff --git a/sys/compat/linuxkpi/common/include/linux/linux_logo.h b/sys/compat/linuxkpi/common/include/linux/linux_logo.h
new file mode 100644
index 000000000000..cb60ba50f6a5
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/linux_logo.h
@@ -0,0 +1,19 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 The FreeBSD Foundation
+ */
+
+#ifndef _LINUXKPI_LINUX_LINUX_LOGO_H_
+#define _LINUXKPI_LINUX_LINUX_LOGO_H_
+
+struct linux_logo {
+ int type;
+ unsigned int width;
+ unsigned int height;
+ unsigned int clutsize;
+ const unsigned char *clut;
+ const unsigned char *data;
+};
+
+#endif /* _LINUXKPI_LINUX_LINUX_LOGO_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/math64.h b/sys/compat/linuxkpi/common/include/linux/math64.h
index 25ca9da1b622..e35aae67fa27 100644
--- a/sys/compat/linuxkpi/common/include/linux/math64.h
+++ b/sys/compat/linuxkpi/common/include/linux/math64.h
@@ -93,6 +93,12 @@ mul_u32_u32(uint32_t a, uint32_t b)
}
static inline uint64_t
+div_u64_round_up(uint64_t dividend, uint32_t divisor)
+{
+ return ((dividend + divisor - 1) / divisor);
+}
+
+static inline uint64_t
div64_u64_round_up(uint64_t dividend, uint64_t divisor)
{
return ((dividend + divisor - 1) / divisor);
@@ -104,6 +110,9 @@ roundup_u64(uint64_t x1, uint32_t x2)
return (div_u64(x1 + x2 - 1, x2) * x2);
}
+#define DIV_U64_ROUND_UP(...) \
+ div_u64_round_up(__VA_ARGS__)
+
#define DIV64_U64_ROUND_UP(...) \
div64_u64_round_up(__VA_ARGS__)
diff --git a/sys/compat/linuxkpi/common/include/linux/mfd/core.h b/sys/compat/linuxkpi/common/include/linux/mfd/core.h
new file mode 100644
index 000000000000..1a69a3803b5d
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/mfd/core.h
@@ -0,0 +1,49 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 The FreeBSD Foundation
+ */
+
+#ifndef _LINUXKPI_LINUX_MFD_CORE_H_
+#define _LINUXKPI_LINUX_MFD_CORE_H_
+
+#include <linux/platform_device.h>
+
+/*
+ * <linux/ioport.h> is not included by Linux, but we need it here to get the
+ * definition of `struct resource`.
+ *
+ * At least the amdgpu DRM driver (amdgpu_isp.c at the time of this writing)
+ * needs the structure without including this header: it relies on an implicit
+ * include of <linux/ioport.h> from <linux/pci.h>, which we can't have due to
+ * conflict with the FreeBSD native `struct resource`.
+ */
+#include <linux/ioport.h>
+
+#include <linux/kernel.h> /* pr_debug */
+
+struct resource;
+struct mfd_cell {
+ const char *name;
+ void *platform_data;
+ size_t pdata_size;
+ int num_resources;
+ const struct resource *resources;
+};
+
+static inline int
+mfd_add_hotplug_devices(struct device *parent,
+ const struct mfd_cell *cells, int n_devs)
+{
+ pr_debug("%s: TODO\n", __func__);
+
+ return (0);
+}
+
+static inline void
+mfd_remove_devices(struct device *parent)
+{
+ pr_debug("%s: TODO\n", __func__);
+}
+
+#endif /* _LINUXKPI_LINUX_MFD_CORE_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/minmax.h b/sys/compat/linuxkpi/common/include/linux/minmax.h
index d48958f0899f..161d0174554a 100644
--- a/sys/compat/linuxkpi/common/include/linux/minmax.h
+++ b/sys/compat/linuxkpi/common/include/linux/minmax.h
@@ -60,6 +60,9 @@
type __max2 = (y); \
__max1 > __max2 ? __max1 : __max2; })
+#define MIN_T(type, x, y) MIN((type)(x), (type)(y))
+#define MAX_T(type, x, y) MAX((type)(x), (type)(y))
+
#define clamp_t(type, _x, min, max) min_t(type, max_t(type, _x, min), max)
#define clamp(x, lo, hi) min(max(x, lo), hi)
#define clamp_val(val, lo, hi) clamp_t(typeof(val), val, lo, hi)
@@ -71,4 +74,17 @@
b = _swap_tmp; \
} while (0)
+/* XXX would have to make sure both are unsigned. */
+#define umin(x, y) MIN(x, y)
+
+/* This macro assumes that the array has elements inside. */
+#define __minmax_array(op, array, len) ({ \
+ typeof(array[0]) __val = array[0]; \
+ for (typeof(len) __i = 1; __i < len; __i++) \
+ __val = op(__val, array[__i]); \
+ __val; })
+
+#define min_array(array, len) __minmax_array(min, array, len)
+#define max_array(array, len) __minmax_array(max, array, len)
+
#endif /* _LINUXKPI_LINUX_MINMAX_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/mm.h b/sys/compat/linuxkpi/common/include/linux/mm.h
index 156b00a0c0f0..a639c0947031 100644
--- a/sys/compat/linuxkpi/common/include/linux/mm.h
+++ b/sys/compat/linuxkpi/common/include/linux/mm.h
@@ -264,6 +264,7 @@ vma_pages(struct vm_area_struct *vma)
}
#define offset_in_page(off) ((unsigned long)(off) & (PAGE_SIZE - 1))
+#define offset_in_folio(folio, p) ((unsigned long)(p) & (folio_size(folio) - 1))
static inline void
set_page_dirty(struct page *page)
diff --git a/sys/compat/linuxkpi/common/include/linux/mod_devicetable.h b/sys/compat/linuxkpi/common/include/linux/mod_devicetable.h
index 87bd6ec24bce..b345b98325e1 100644
--- a/sys/compat/linuxkpi/common/include/linux/mod_devicetable.h
+++ b/sys/compat/linuxkpi/common/include/linux/mod_devicetable.h
@@ -30,6 +30,7 @@
#define __LINUXKPI_LINUX_MOD_DEVICETABLE_H__
#include <linux/types.h>
+#include <linux/uuid.h>
enum dmi_field {
DMI_NONE,
@@ -80,4 +81,10 @@ struct dmi_system_id {
#define ACPI_ID_LEN 16
+/* -----------------------------------------------------------------------------
+ * USB
+ */
+/* struct usb_device_id is defined in sys/dev/usb/usbdi.h. */
+/* MODULE_DEVICE_TABLE_BUS_usb we have in usb.h. */
+
#endif /* __LINUXKPI_LINUX_MOD_DEVICETABLE_H__ */
diff --git a/sys/compat/linuxkpi/common/include/linux/module.h b/sys/compat/linuxkpi/common/include/linux/module.h
index 079dacf8df6c..fbe57cbbed82 100644
--- a/sys/compat/linuxkpi/common/include/linux/module.h
+++ b/sys/compat/linuxkpi/common/include/linux/module.h
@@ -53,6 +53,24 @@
#define MODULE_SUPPORTED_DEVICE(name)
#define MODULE_IMPORT_NS(_name)
+/* Linux has an empty element at the end of the ID table -> nitems() - 1. */
+#define MODULE_DEVICE_TABLE(_bus, _table) \
+ \
+static device_method_t _ ## _bus ## _ ## _table ## _methods[] = { \
+ DEVMETHOD_END \
+}; \
+ \
+static driver_t _ ## _bus ## _ ## _table ## _driver = { \
+ "lkpi_" #_bus #_table, \
+ _ ## _bus ## _ ## _table ## _methods, \
+ 0 \
+}; \
+ \
+DRIVER_MODULE(lkpi_ ## _table, _bus, _ ## _bus ## _ ## _table ## _driver,\
+ 0, 0); \
+ \
+MODULE_DEVICE_TABLE_BUS_ ## _bus(_bus, _table)
+
/*
* THIS_MODULE is used to differentiate modules on Linux. We currently
* completely stub out any Linux struct module usage, but THIS_MODULE is still
diff --git a/sys/compat/linuxkpi/common/include/linux/mutex.h b/sys/compat/linuxkpi/common/include/linux/mutex.h
index 6fb6a7744a89..64d76491a76f 100644
--- a/sys/compat/linuxkpi/common/include/linux/mutex.h
+++ b/sys/compat/linuxkpi/common/include/linux/mutex.h
@@ -174,4 +174,6 @@ linux_mutex_destroy(mutex_t *m)
extern int linux_mutex_lock_interruptible(mutex_t *m);
+DEFINE_GUARD(mutex, struct mutex *, mutex_lock(_T), mutex_unlock(_T))
+
#endif /* _LINUXKPI_LINUX_MUTEX_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/netdevice.h b/sys/compat/linuxkpi/common/include/linux/netdevice.h
index cf27753bcb80..dfed5fbd61b4 100644
--- a/sys/compat/linuxkpi/common/include/linux/netdevice.h
+++ b/sys/compat/linuxkpi/common/include/linux/netdevice.h
@@ -160,6 +160,30 @@ struct net_device {
#define SET_NETDEV_DEV(_ndev, _dev) (_ndev)->dev.parent = _dev;
+enum net_device_path_type {
+ DEV_PATH_MTK_WDMA,
+};
+
+struct net_device_path {
+ enum net_device_path_type type;
+ const struct net_device *dev;
+ /* We assume there's a struct per type. */
+ union {
+ struct {
+ uint16_t wcid;
+ uint8_t wdma_idx;
+ uint8_t queue;
+ uint8_t bss;
+ uint8_t amsdu;
+ } mtk_wdma;
+ };
+};
+
+struct net_device_path_ctx {
+ const struct net_device *dev;
+};
+
+
/* -------------------------------------------------------------------------- */
/* According to linux::ipoib_main.c. */
struct netdev_notifier_info {
diff --git a/sys/compat/linuxkpi/common/include/linux/overflow.h b/sys/compat/linuxkpi/common/include/linux/overflow.h
index e811037b8ecc..4326f05e6d07 100644
--- a/sys/compat/linuxkpi/common/include/linux/overflow.h
+++ b/sys/compat/linuxkpi/common/include/linux/overflow.h
@@ -4,9 +4,7 @@
#include <linux/compiler.h>
#include <linux/limits.h>
-#ifdef __linux__
#include <linux/const.h>
-#endif
/*
* We need to compute the minimum and maximum values representable in a given
@@ -38,19 +36,13 @@
#define __type_min(T) ((T)((T)-type_max(T)-(T)1))
#define type_min(t) __type_min(typeof(t))
-/*
- * Avoids triggering -Wtype-limits compilation warning,
- * while using unsigned data types to check a < 0.
- */
-#define is_non_negative(a) ((a) > 0 || (a) == 0)
-#define is_negative(a) (!(is_non_negative(a)))
/*
* Allows for effectively applying __must_check to a macro so we can have
* both the type-agnostic benefits of the macros while also being able to
* enforce that the return value is, in fact, checked.
*/
-static inline bool __must_check __must_check_overflow(bool overflow)
+static __always_inline bool __must_check __must_check_overflow(bool overflow)
{
return unlikely(overflow);
}
@@ -203,9 +195,9 @@ static inline bool __must_check __must_check_overflow(bool overflow)
typeof(d) _d = d; \
unsigned long long _a_full = _a; \
unsigned int _to_shift = \
- is_non_negative(_s) && _s < 8 * sizeof(*d) ? _s : 0; \
+ _s >= 0 && _s < 8 * sizeof(*d) ? _s : 0; \
*_d = (_a_full << _to_shift); \
- (_to_shift != _s || is_negative(*_d) || is_negative(_a) || \
+ (_to_shift != _s || *_d < 0 || _a < 0 || \
(*_d >> _to_shift) != _a); \
}))
@@ -241,6 +233,76 @@ static inline bool __must_check __must_check_overflow(bool overflow)
__overflows_type(n, T))
/**
+ * range_overflows() - Check if a range is out of bounds
+ * @start: Start of the range.
+ * @size: Size of the range.
+ * @max: Exclusive upper boundary.
+ *
+ * A strict check to determine if the range [@start, @start + @size) is
+ * invalid with respect to the allowable range [0, @max). Any range
+ * starting at or beyond @max is considered an overflow, even if @size is 0.
+ *
+ * Returns: true if the range is out of bounds.
+ */
+#define range_overflows(start, size, max) ({ \
+ typeof(start) start__ = (start); \
+ typeof(size) size__ = (size); \
+ typeof(max) max__ = (max); \
+ (void)(&start__ == &size__); \
+ (void)(&start__ == &max__); \
+ start__ >= max__ || size__ > max__ - start__; \
+})
+
+/**
+ * range_overflows_t() - Check if a range is out of bounds
+ * @type: Data type to use.
+ * @start: Start of the range.
+ * @size: Size of the range.
+ * @max: Exclusive upper boundary.
+ *
+ * Same as range_overflows() but forcing the parameters to @type.
+ *
+ * Returns: true if the range is out of bounds.
+ */
+#define range_overflows_t(type, start, size, max) \
+ range_overflows((type)(start), (type)(size), (type)(max))
+
+/**
+ * range_end_overflows() - Check if a range's endpoint is out of bounds
+ * @start: Start of the range.
+ * @size: Size of the range.
+ * @max: Exclusive upper boundary.
+ *
+ * Checks only if the endpoint of a range (@start + @size) exceeds @max.
+ * Unlike range_overflows(), a zero-sized range at the boundary (@start == @max)
+ * is not considered an overflow. Useful for iterator-style checks.
+ *
+ * Returns: true if the endpoint exceeds the boundary.
+ */
+#define range_end_overflows(start, size, max) ({ \
+ typeof(start) start__ = (start); \
+ typeof(size) size__ = (size); \
+ typeof(max) max__ = (max); \
+ (void)(&start__ == &size__); \
+ (void)(&start__ == &max__); \
+ start__ > max__ || size__ > max__ - start__; \
+})
+
+/**
+ * range_end_overflows_t() - Check if a range's endpoint is out of bounds
+ * @type: Data type to use.
+ * @start: Start of the range.
+ * @size: Size of the range.
+ * @max: Exclusive upper boundary.
+ *
+ * Same as range_end_overflows() but forcing the parameters to @type.
+ *
+ * Returns: true if the endpoint exceeds the boundary.
+ */
+#define range_end_overflows_t(type, start, size, max) \
+ range_end_overflows((type)(start), (type)(size), (type)(max))
+
+/**
* castable_to_type - like __same_type(), but also allows for casted literals
*
* @n: variable or constant value
@@ -265,7 +327,7 @@ static inline bool __must_check __must_check_overflow(bool overflow)
* with any overflow causing the return value to be SIZE_MAX. The
* lvalue must be size_t to avoid implicit type conversion.
*/
-static inline size_t __must_check size_mul(size_t factor1, size_t factor2)
+static __always_inline size_t __must_check size_mul(size_t factor1, size_t factor2)
{
size_t bytes;
@@ -284,7 +346,7 @@ static inline size_t __must_check size_mul(size_t factor1, size_t factor2)
* with any overflow causing the return value to be SIZE_MAX. The
* lvalue must be size_t to avoid implicit type conversion.
*/
-static inline size_t __must_check size_add(size_t addend1, size_t addend2)
+static __always_inline size_t __must_check size_add(size_t addend1, size_t addend2)
{
size_t bytes;
@@ -305,7 +367,7 @@ static inline size_t __must_check size_add(size_t addend1, size_t addend2)
* argument may be SIZE_MAX (or the result with be forced to SIZE_MAX).
* The lvalue must be size_t to avoid implicit type conversion.
*/
-static inline size_t __must_check size_sub(size_t minuend, size_t subtrahend)
+static __always_inline size_t __must_check size_sub(size_t minuend, size_t subtrahend)
{
size_t bytes;
@@ -391,6 +453,18 @@ static inline size_t __must_check size_sub(size_t minuend, size_t subtrahend)
struct_size((type *)NULL, member, count)
/**
+ * struct_offset() - Calculate the offset of a member within a struct
+ * @p: Pointer to the struct
+ * @member: Name of the member to get the offset of
+ *
+ * Calculates the offset of a particular @member of the structure pointed
+ * to by @p.
+ *
+ * Return: number of bytes to the location of @member.
+ */
+#define struct_offset(p, member) (offsetof(typeof(*(p)), member))
+
+/**
* __DEFINE_FLEX() - helper macro for DEFINE_FLEX() family.
* Enables caller macro to pass arbitrary trailing expressions
*
@@ -472,4 +546,46 @@ static inline size_t __must_check size_sub(size_t minuend, size_t subtrahend)
(__member_size((name)->array) / sizeof(*(name)->array) + \
__must_be_array((name)->array))
+/**
+ * typeof_flex_counter() - Return the type of the counter variable of a given
+ * flexible array member annotated by __counted_by().
+ * @FAM: Instance of flexible array member within a given struct.
+ *
+ * Returns: "size_t" if no annotation exists.
+ */
+#define typeof_flex_counter(FAM) \
+ typeof(_Generic(__flex_counter(FAM), \
+ void *: (size_t)0, \
+ default: *__flex_counter(FAM)))
+
+/**
+ * overflows_flex_counter_type() - Check if the counter associated with the
+ * given flexible array member can represent
+ * a value.
+ * @TYPE: Type of the struct that contains the @FAM.
+ * @FAM: Member name of the FAM within @TYPE.
+ * @COUNT: Value to check against the __counted_by annotated @FAM's counter.
+ *
+ * Returns: true if @COUNT can be represented in the @FAM's counter. When
+ * @FAM is not annotated with __counted_by(), always returns true.
+ */
+#define overflows_flex_counter_type(TYPE, FAM, COUNT) \
+ (overflows_type(COUNT, typeof_flex_counter(((TYPE *)NULL)->FAM)))
+
+/**
+ * __set_flex_counter() - Set the counter associated with the given flexible
+ * array member that has been annoated by __counted_by().
+ * @FAM: Instance of flexible array member within a given struct.
+ * @COUNT: Value to store to the __counted_by annotated @FAM_PTR's counter.
+ *
+ * This is a no-op if no annotation exists. Count needs to be checked with
+ * overflows_flex_counter_type() before using this function.
+ */
+#define __set_flex_counter(FAM, COUNT) \
+({ \
+ *_Generic(__flex_counter(FAM), \
+ void *: &(size_t){ 0 }, \
+ default: __flex_counter(FAM)) = (COUNT); \
+})
+
#endif /* _LINUXKPI_LINUX_OVERFLOW_H */
diff --git a/sys/compat/linuxkpi/common/include/linux/pci.h b/sys/compat/linuxkpi/common/include/linux/pci.h
index c337be67f5a4..5cd87ff091bc 100644
--- a/sys/compat/linuxkpi/common/include/linux/pci.h
+++ b/sys/compat/linuxkpi/common/include/linux/pci.h
@@ -60,6 +60,17 @@
#include <linux/pci_ids.h>
#include <linux/pm.h>
+/*
+ * <linux/ioport.h> should be included here, like Linux, but we can't have that
+ * because Linux `struct resource` definition would conflict with FreeBSD
+ * native definition.
+ *
+ * At least the amdgpu DRM driver (amdgpu_isp.c at the time of this writing)
+ * relies on this indirect include to get the definition of Linux `struct
+ * resource`. As a workaround, we include <linux/ioport.h> from
+ * <linux/mfd/core.h>.
+ */
+
#include <linux/kernel.h> /* pr_debug */
struct pci_device_id {
@@ -76,24 +87,6 @@ struct pci_device_id {
MODULE_PNP_INFO("U32:vendor;U32:device;V32:subvendor;V32:subdevice", \
_bus, lkpi_ ## _table, _table, nitems(_table) - 1)
-/* Linux has an empty element at the end of the ID table -> nitems() - 1. */
-#define MODULE_DEVICE_TABLE(_bus, _table) \
- \
-static device_method_t _ ## _bus ## _ ## _table ## _methods[] = { \
- DEVMETHOD_END \
-}; \
- \
-static driver_t _ ## _bus ## _ ## _table ## _driver = { \
- "lkpi_" #_bus #_table, \
- _ ## _bus ## _ ## _table ## _methods, \
- 0 \
-}; \
- \
-DRIVER_MODULE(lkpi_ ## _table, _bus, _ ## _bus ## _ ## _table ## _driver,\
- 0, 0); \
- \
-MODULE_DEVICE_TABLE_BUS_ ## _bus(_bus, _table)
-
#define PCI_ANY_ID -1U
#define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07))
@@ -1340,6 +1333,13 @@ pci_dev_present(const struct pci_device_id *cur)
return (0);
}
+static inline bool
+pci_dev_is_disconnected(const struct pci_dev *pdev)
+{
+ pr_debug("TODO: %s\n", __func__);
+ return (false);
+}
+
static inline const struct pci_device_id *
pci_match_id(const struct pci_device_id *ids, struct pci_dev *pdev)
{
diff --git a/sys/compat/linuxkpi/common/include/linux/platform_data/brcmfmac.h b/sys/compat/linuxkpi/common/include/linux/platform_data/brcmfmac.h
new file mode 100644
index 000000000000..ec99b7b73d1d
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/platform_data/brcmfmac.h
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2016 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _LINUX_BRCMFMAC_PLATFORM_H
+#define _LINUX_BRCMFMAC_PLATFORM_H
+
+
+#define BRCMFMAC_PDATA_NAME "brcmfmac"
+
+#define BRCMFMAC_COUNTRY_BUF_SZ 4
+
+
+/*
+ * Platform specific driver functions and data. Through the platform specific
+ * device data functions and data can be provided to help the brcmfmac driver to
+ * operate with the device in combination with the used platform.
+ */
+
+
+/**
+ * Note: the brcmfmac can be loaded as module or be statically built-in into
+ * the kernel. If built-in then do note that it uses module_init (and
+ * module_exit) routines which equal device_initcall. So if you intend to
+ * create a module with the platform specific data for the brcmfmac and have
+ * it built-in to the kernel then use a higher initcall then device_initcall
+ * (see init.h). If this is not done then brcmfmac will load without problems
+ * but will not pickup the platform data.
+ *
+ * When the driver does not "detect" platform driver data then it will continue
+ * without reporting anything and just assume there is no data needed. Which is
+ * probably true for most platforms.
+ */
+
+/**
+ * enum brcmf_bus_type - Bus type identifier. Currently SDIO, USB and PCIE are
+ * supported.
+ */
+enum brcmf_bus_type {
+ BRCMF_BUSTYPE_SDIO,
+ BRCMF_BUSTYPE_USB,
+ BRCMF_BUSTYPE_PCIE
+};
+
+
+/**
+ * struct brcmfmac_sdio_pd - SDIO Device specific platform data.
+ *
+ * @txglomsz: SDIO txglom size. Use 0 if default of driver is to be
+ * used.
+ * @drive_strength: is the preferred drive_strength to be used for the SDIO
+ * pins. If 0 then a default value will be used. This is
+ * the target drive strength, the exact drive strength
+ * which will be used depends on the capabilities of the
+ * device.
+ * @oob_irq_supported: does the board have support for OOB interrupts. SDIO
+ * in-band interrupts are relatively slow and for having
+ * less overhead on interrupt processing an out of band
+ * interrupt can be used. If the HW supports this then
+ * enable this by setting this field to true and configure
+ * the oob related fields.
+ * @oob_irq_nr,
+ * @oob_irq_flags: the OOB interrupt information. The values are used for
+ * registering the irq using request_irq function.
+ * @broken_sg_support: flag for broken sg list support of SDIO host controller.
+ * Set this to true if the SDIO host controller has higher
+ * align requirement than 32 bytes for each scatterlist
+ * item.
+ * @sd_head_align: alignment requirement for start of data buffer.
+ * @sd_sgentry_align: length alignment requirement for each sg entry.
+ * @reset: This function can get called if the device communication
+ * broke down. This functionality is particularly useful in
+ * case of SDIO type devices. It is possible to reset a
+ * dongle via sdio data interface, but it requires that
+ * this is fully functional. This function is chip/module
+ * specific and this function should return only after the
+ * complete reset has completed.
+ */
+struct brcmfmac_sdio_pd {
+ int txglomsz;
+ unsigned int drive_strength;
+ bool oob_irq_supported;
+ unsigned int oob_irq_nr;
+ unsigned long oob_irq_flags;
+ bool broken_sg_support;
+ unsigned short sd_head_align;
+ unsigned short sd_sgentry_align;
+ void (*reset)(void);
+};
+
+/**
+ * struct brcmfmac_pd_cc_entry - Struct for translating user space country code
+ * (iso3166) to firmware country code and
+ * revision.
+ *
+ * @iso3166: iso3166 alpha 2 country code string.
+ * @cc: firmware country code string.
+ * @rev: firmware country code revision.
+ */
+struct brcmfmac_pd_cc_entry {
+ char iso3166[BRCMFMAC_COUNTRY_BUF_SZ];
+ char cc[BRCMFMAC_COUNTRY_BUF_SZ];
+ s32 rev;
+};
+
+/**
+ * struct brcmfmac_pd_cc - Struct for translating country codes as set by user
+ * space to a country code and rev which can be used by
+ * firmware.
+ *
+ * @table_size: number of entries in table (> 0)
+ * @table: array of 1 or more elements with translation information.
+ */
+struct brcmfmac_pd_cc {
+ int table_size;
+ struct brcmfmac_pd_cc_entry table[];
+};
+
+/**
+ * struct brcmfmac_pd_device - Device specific platform data. (id/rev/bus_type)
+ * is the unique identifier of the device.
+ *
+ * @id: ID of the device for which this data is. In case of SDIO
+ * or PCIE this is the chipid as identified by chip.c In
+ * case of USB this is the chipid as identified by the
+ * device query.
+ * @rev: chip revision, see id.
+ * @bus_type: The type of bus. Some chipid/rev exist for different bus
+ * types. Each bus type has its own set of settings.
+ * @feature_disable: Bitmask of features to disable (override), See feature.c
+ * in brcmfmac for details.
+ * @country_codes: If available, pointer to struct for translating country
+ * codes.
+ * @bus: Bus specific (union) device settings. Currently only
+ * SDIO.
+ */
+struct brcmfmac_pd_device {
+ unsigned int id;
+ unsigned int rev;
+ enum brcmf_bus_type bus_type;
+ unsigned int feature_disable;
+ struct brcmfmac_pd_cc *country_codes;
+ union {
+ struct brcmfmac_sdio_pd sdio;
+ } bus;
+};
+
+/**
+ * struct brcmfmac_platform_data - BRCMFMAC specific platform data.
+ *
+ * @power_on: This function is called by the brcmfmac driver when the module
+ * gets loaded. This can be particularly useful for low power
+ * devices. The platform spcific routine may for example decide to
+ * power up the complete device. If there is no use-case for this
+ * function then provide NULL.
+ * @power_off: This function is called by the brcmfmac when the module gets
+ * unloaded. At this point the devices can be powered down or
+ * otherwise be reset. So if an actual power_off is not supported
+ * but reset is supported by the devices then reset the devices
+ * when this function gets called. This can be particularly useful
+ * for low power devices. If there is no use-case for this
+ * function then provide NULL.
+ */
+struct brcmfmac_platform_data {
+ void (*power_on)(void);
+ void (*power_off)(void);
+ char *fw_alternative_path;
+ int device_count;
+ struct brcmfmac_pd_device devices[];
+};
+
+
+#endif /* _LINUX_BRCMFMAC_PLATFORM_H */
diff --git a/sys/compat/linuxkpi/common/include/linux/ptp_clock_kernel.h b/sys/compat/linuxkpi/common/include/linux/ptp_clock_kernel.h
index aad46cc25b1b..6491cbeab7e2 100644
--- a/sys/compat/linuxkpi/common/include/linux/ptp_clock_kernel.h
+++ b/sys/compat/linuxkpi/common/include/linux/ptp_clock_kernel.h
@@ -49,6 +49,7 @@ struct ptp_clock_info {
int (*adjtime)(struct ptp_clock_info *, s64);
int (*getcrosststamp)(struct ptp_clock_info *, struct system_device_crosststamp *);
int (*gettime64)(struct ptp_clock_info *, struct timespec *);
+ int (*settime64)(struct ptp_clock_info *, const struct timespec *);
};
static inline struct ptp_clock *
diff --git a/sys/compat/linuxkpi/common/include/linux/radix-tree.h b/sys/compat/linuxkpi/common/include/linux/radix-tree.h
index 55f0fc949807..7182f4a9e407 100644
--- a/sys/compat/linuxkpi/common/include/linux/radix-tree.h
+++ b/sys/compat/linuxkpi/common/include/linux/radix-tree.h
@@ -38,12 +38,19 @@
#define RADIX_TREE_MAX_HEIGHT \
howmany(sizeof(long) * NBBY, RADIX_TREE_MAP_SHIFT)
+#define RADIX_TREE_MAX_TAGS 3
+#define RADIX_TREE_TAG_LONGS RADIX_TREE_MAP_SIZE
+
#define RADIX_TREE_ENTRY_MASK 3UL
#define RADIX_TREE_EXCEPTIONAL_ENTRY 2UL
#define RADIX_TREE_EXCEPTIONAL_SHIFT 2
+#define RADIX_TREE_ITER_TAG_MASK 0x0f
+#define RADIX_TREE_ITER_TAGGED 0x10
+
struct radix_tree_node {
void *slots[RADIX_TREE_MAP_SIZE];
+ unsigned long tags[RADIX_TREE_MAX_TAGS][RADIX_TREE_TAG_LONGS];
int count;
};
@@ -51,6 +58,8 @@ struct radix_tree_root {
struct radix_tree_node *rnode;
gfp_t gfp_mask;
int height;
+ /* Linux stores root tags inside `gfp_mask`. */
+ unsigned long tags[RADIX_TREE_MAX_TAGS];
};
struct radix_tree_iter {
@@ -64,9 +73,16 @@ struct radix_tree_iter {
#define RADIX_TREE(name, mask) \
struct radix_tree_root name = RADIX_TREE_INIT(mask)
-#define radix_tree_for_each_slot(slot, root, iter, start) \
- for ((iter)->index = (start); \
- radix_tree_iter_find(root, iter, &(slot)); (iter)->index++)
+#define radix_tree_for_each_slot(slot, root, iter, start) \
+ for ((iter)->index = (start); \
+ radix_tree_iter_find(root, iter, &(slot), 0); \
+ (iter)->index++)
+
+#define radix_tree_for_each_slot_tagged(slot, root, iter, start, tag) \
+ for ((iter)->index = (start); \
+ radix_tree_iter_find(root, iter, &(slot), \
+ RADIX_TREE_ITER_TAGGED | tag); \
+ (iter)->index++)
static inline void *
radix_tree_deref_slot(void **slot)
@@ -84,7 +100,12 @@ void *radix_tree_lookup(const struct radix_tree_root *, unsigned long);
void *radix_tree_delete(struct radix_tree_root *, unsigned long);
int radix_tree_insert(struct radix_tree_root *, unsigned long, void *);
int radix_tree_store(struct radix_tree_root *, unsigned long, void **);
-bool radix_tree_iter_find(const struct radix_tree_root *, struct radix_tree_iter *, void ***);
+bool radix_tree_iter_find(const struct radix_tree_root *, struct radix_tree_iter *, void ***, int);
void radix_tree_iter_delete(struct radix_tree_root *, struct radix_tree_iter *, void **);
+void *radix_tree_tag_set(struct radix_tree_root *root, unsigned long index, unsigned int tag);
+void *radix_tree_tag_clear(struct radix_tree_root *root, unsigned long index, unsigned int tag);
+int radix_tree_tagged(const struct radix_tree_root *root, unsigned int tag);
+unsigned int radix_tree_gang_lookup(const struct radix_tree_root *root, void **results, unsigned long first_index, unsigned int max_items);
+unsigned int radix_tree_gang_lookup_tag(const struct radix_tree_root *root, void **results, unsigned long first_index, unsigned int max_items, unsigned int tag);
#endif /* _LINUXKPI_LINUX_RADIX_TREE_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/rbtree.h b/sys/compat/linuxkpi/common/include/linux/rbtree.h
index e6033cfd760d..834e5645e991 100644
--- a/sys/compat/linuxkpi/common/include/linux/rbtree.h
+++ b/sys/compat/linuxkpi/common/include/linux/rbtree.h
@@ -199,6 +199,26 @@ rb_add_cached(struct rb_node *node, struct rb_root_cached *tree,
return (leftmost ? node : NULL);
}
+static inline void
+rb_add(struct rb_node *node, struct rb_root *tree,
+ bool (*less)(struct rb_node *, const struct rb_node *))
+{
+ struct rb_node **link = &tree->rb_node;
+ struct rb_node *parent = NULL;
+
+ while (*link != NULL) {
+ parent = *link;
+ if (less(node, parent)) {
+ link = &RB_LEFT(parent, __entry);
+ } else {
+ link = &RB_RIGHT(parent, __entry);
+ }
+ }
+
+ rb_link_node(node, parent, link);
+ rb_insert_color(node, tree);
+}
+
#undef RB_ROOT
#define RB_ROOT (struct rb_root) { NULL }
#define RB_ROOT_CACHED (struct rb_root_cached) { RB_ROOT, NULL }
diff --git a/sys/compat/linuxkpi/common/include/linux/scatterlist.h b/sys/compat/linuxkpi/common/include/linux/scatterlist.h
index 537f5bebc5aa..c49c2179d359 100644
--- a/sys/compat/linuxkpi/common/include/linux/scatterlist.h
+++ b/sys/compat/linuxkpi/common/include/linux/scatterlist.h
@@ -654,9 +654,9 @@ sg_pcopy_to_buffer(struct scatterlist *sgl, unsigned int nents,
sf = sf_buf_alloc(page, SFB_CPUPRIVATE | SFB_NOWAIT);
if (sf == NULL)
break;
- vaddr = (char *)sf_buf_kva(sf);
+ vaddr = sf_buf_kva(sf);
} else
- vaddr = (char *)PHYS_TO_DMAP(page_to_phys(page));
+ vaddr = PHYS_TO_DMAP(page_to_phys(page));
memcpy(buf, vaddr + sg->offset + offset, len);
if (!PMAP_HAS_DMAP)
sf_buf_free(sf);
diff --git a/sys/compat/linuxkpi/common/include/linux/seq_buf.h b/sys/compat/linuxkpi/common/include/linux/seq_buf.h
new file mode 100644
index 000000000000..d6246a40e6f7
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/seq_buf.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2025-2026 The FreeBSD Foundation
+ * Copyright (c) 2025-2026 Jean-Sébastien Pédron <dumbbell@FreeBSD.org>
+ *
+ * This software was developed by Jean-Sébastien Pédron under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef _LINUXKPI_LINUX_SEQ_BUF_H_
+#define _LINUXKPI_LINUX_SEQ_BUF_H_
+
+#include <linux/bug.h>
+#include <linux/minmax.h>
+#include <linux/seq_file.h>
+#include <linux/types.h>
+
+struct seq_buf {
+ char *buffer;
+ size_t size;
+ size_t len;
+};
+
+#define DECLARE_SEQ_BUF(NAME, SIZE) \
+ struct seq_buf NAME = { \
+ .buffer = (char[SIZE]) { 0 }, \
+ .size = SIZE, \
+ }
+
+static inline void
+seq_buf_clear(struct seq_buf *s)
+{
+ s->len = 0;
+ if (s->size > 0)
+ s->buffer[0] = '\0';
+}
+
+static inline void
+seq_buf_set_overflow(struct seq_buf *s)
+{
+ s->len = s->size + 1;
+}
+
+static inline bool
+seq_buf_has_overflowed(struct seq_buf *s)
+{
+ return (s->len > s->size);
+}
+
+static inline bool
+seq_buf_buffer_left(struct seq_buf *s)
+{
+ if (seq_buf_has_overflowed(s))
+ return (0);
+
+ return (s->size - s->len);
+}
+
+#define seq_buf_init(s, buf, size) linuxkpi_seq_buf_init((s), (buf), (size))
+void linuxkpi_seq_buf_init(struct seq_buf *s, char *buf, unsigned int size);
+
+#define seq_buf_printf(s, f, ...) linuxkpi_seq_buf_printf((s), (f), __VA_ARGS__)
+int linuxkpi_seq_buf_printf(struct seq_buf *s, const char *fmt, ...) \
+ __printflike(2, 3);
+
+#define seq_buf_vprintf(s, f, a) linuxkpi_seq_buf_vprintf((s), (f), (a))
+int linuxkpi_seq_buf_vprintf(struct seq_buf *s, const char *fmt, va_list args);
+
+#define seq_buf_str(s) linuxkpi_seq_buf_str((s))
+const char * linuxkpi_seq_buf_str(struct seq_buf *s);
+
+#endif
diff --git a/sys/compat/linuxkpi/common/include/linux/seq_file.h b/sys/compat/linuxkpi/common/include/linux/seq_file.h
index 3c7862890c67..be03c4768b07 100644
--- a/sys/compat/linuxkpi/common/include/linux/seq_file.h
+++ b/sys/compat/linuxkpi/common/include/linux/seq_file.h
@@ -39,6 +39,8 @@
#undef file
#define inode vnode
+#define SEQ_SKIP 1
+
MALLOC_DECLARE(M_LSEQ);
#define DEFINE_SHOW_ATTRIBUTE(__name) \
@@ -85,7 +87,7 @@ struct seq_operations {
int (*show) (struct seq_file *m, void *v);
};
-ssize_t seq_read(struct linux_file *, char *, size_t, off_t *);
+ssize_t seq_read(struct linux_file *, char __user *, size_t, off_t *);
int seq_write(struct seq_file *seq, const void *data, size_t len);
void seq_putc(struct seq_file *m, char c);
void seq_puts(struct seq_file *m, const char *str);
diff --git a/sys/compat/linuxkpi/common/include/linux/sizes.h b/sys/compat/linuxkpi/common/include/linux/sizes.h
index d8a6e75192f6..430ad3ec427d 100644
--- a/sys/compat/linuxkpi/common/include/linux/sizes.h
+++ b/sys/compat/linuxkpi/common/include/linux/sizes.h
@@ -29,6 +29,17 @@
#ifndef _LINUXKPI_LINUX_SIZES_H_
#define _LINUXKPI_LINUX_SIZES_H_
+#define SZ_1 1
+#define SZ_2 2
+#define SZ_4 4
+#define SZ_8 8
+#define SZ_16 16
+#define SZ_32 32
+#define SZ_64 64
+#define SZ_128 128
+#define SZ_256 256
+#define SZ_512 512
+
#define SZ_1K (1024 * 1)
#define SZ_2K (1024 * 2)
#define SZ_4K (1024 * 4)
diff --git a/sys/compat/linuxkpi/common/include/linux/skbuff.h b/sys/compat/linuxkpi/common/include/linux/skbuff.h
index 2e560a120e41..c43d6daff5ee 100644
--- a/sys/compat/linuxkpi/common/include/linux/skbuff.h
+++ b/sys/compat/linuxkpi/common/include/linux/skbuff.h
@@ -770,7 +770,7 @@ ___skb_queue_splice(const struct sk_buff_head *from,
}
static inline void
-skb_queue_splice_init(struct sk_buff_head *from, struct sk_buff_head *to)
+skb_queue_splice(const struct sk_buff_head *from, struct sk_buff_head *to)
{
SKB_TRACE2(from, to);
@@ -780,6 +780,13 @@ skb_queue_splice_init(struct sk_buff_head *from, struct sk_buff_head *to)
___skb_queue_splice(from, (struct sk_buff *)to, to->next);
to->qlen += from->qlen;
+}
+
+static inline void
+skb_queue_splice_init(struct sk_buff_head *from, struct sk_buff_head *to)
+{
+
+ skb_queue_splice(from, to);
__skb_queue_head_init(from);
}
diff --git a/sys/compat/linuxkpi/common/include/linux/slab.h b/sys/compat/linuxkpi/common/include/linux/slab.h
index 0e649e1e3c4a..b529a6303085 100644
--- a/sys/compat/linuxkpi/common/include/linux/slab.h
+++ b/sys/compat/linuxkpi/common/include/linux/slab.h
@@ -4,7 +4,7 @@
* Copyright (c) 2010 Panasas, Inc.
* Copyright (c) 2013-2021 Mellanox Technologies, Ltd.
* All rights reserved.
- * Copyright (c) 2024-2025 The FreeBSD Foundation
+ * Copyright (c) 2024-2026 The FreeBSD Foundation
*
* Portions of this software were developed by Björn Zeeb
* under sponsorship from the FreeBSD Foundation.
@@ -51,6 +51,22 @@ MALLOC_DECLARE(M_KMALLOC);
#define kvcalloc(n, size, flags) kvmalloc_array(n, size, (flags) | __GFP_ZERO)
#define kzalloc(size, flags) kmalloc(size, (flags) | __GFP_ZERO)
#define kzalloc_node(size, flags, node) kmalloc_node(size, (flags) | __GFP_ZERO, node)
+#define kzalloc_obj(_p, ...) \
+ kzalloc(sizeof(typeof(_p)), default_gfp(__VA_ARGS__))
+#define kzalloc_objs(_p, _n, ...) \
+ kzalloc(size_mul((_n), sizeof(typeof(_p))), default_gfp(__VA_ARGS__))
+#define kzalloc_flex(_p, _field, _n, ...) \
+({ \
+ const size_t __n = (_n); \
+ const size_t __psize = struct_size_t(typeof(_p), _field, __n); \
+ typeof(_p) *__p_obj; \
+ \
+ __p_obj = kzalloc(__psize, default_gfp(__VA_ARGS__)); \
+ if (__p_obj != NULL) \
+ __set_flex_counter(__p_obj->_field, __n); \
+ \
+ __p_obj; \
+})
#define kfree_const(ptr) kfree(ptr)
#define kfree_async(ptr) kfree(ptr) /* drm-kmod 5.4 compat */
#define vzalloc(size) __vmalloc(size, GFP_KERNEL | __GFP_NOWARN | __GFP_ZERO, 0)
@@ -104,7 +120,7 @@ void *lkpi_kmalloc(size_t, gfp_t);
void *lkpi_kvmalloc(size_t, gfp_t);
void *lkpi___kmalloc(size_t, gfp_t);
void *lkpi___kmalloc_node(size_t, gfp_t, int);
-void *lkpi_krealloc(void *, size_t, gfp_t);
+void *lkpi_krealloc(const void *, size_t, gfp_t);
void lkpi_kfree(const void *);
static inline gfp_t
@@ -125,6 +141,13 @@ linux_check_m_flags(gfp_t flags)
/*
* Base functions with a native implementation.
*/
+
+static inline size_t
+ksize(const void *ptr)
+{
+ return (malloc_usable_size(ptr));
+}
+
static inline void *
kmalloc(size_t size, gfp_t flags)
{
@@ -143,8 +166,14 @@ kmalloc_node(size_t size, gfp_t flags, int node)
return (lkpi___kmalloc_node(size, flags, node));
}
+#define kmalloc_obj(_p, ...) \
+ kmalloc(sizeof(typeof(_p)), default_gfp(__VA_ARGS__))
+
+#define kmalloc_objs(_p, _n, ...) \
+ kmalloc(size_mul((_n) * sizeof(typeof(_p))), default_gfp(__VA_ARGS__))
+
static inline void *
-krealloc(void *ptr, size_t size, gfp_t flags)
+krealloc(const void *ptr, size_t size, gfp_t flags)
{
return (lkpi_krealloc(ptr, size, flags));
}
@@ -242,22 +271,28 @@ kvmalloc_array(size_t n, size_t size, gfp_t flags)
return (kvmalloc(size * n, flags));
}
+void * lkpi_kvrealloc(const void *ptr, size_t oldsize, size_t newsize, gfp_t flags);
+
+#if defined(LINUXKPI_VERSION) && LINUXKPI_VERSION < 61200
static inline void *
kvrealloc(const void *ptr, size_t oldsize, size_t newsize, gfp_t flags)
{
- void *newptr;
-
- if (newsize <= oldsize)
- return (__DECONST(void *, ptr));
+ return (lkpi_kvrealloc(ptr, oldsize, newsize, flags));
+}
+#else
+static inline void *
+kvrealloc(const void *ptr, size_t newsize, gfp_t flags)
+{
+ size_t oldsize;
- newptr = kvmalloc(newsize, flags);
- if (newptr != NULL) {
- memcpy(newptr, ptr, oldsize);
- kvfree(ptr);
- }
+ if (!ZERO_OR_NULL_PTR(ptr))
+ oldsize = ksize(ptr);
+ else
+ oldsize = 0;
- return (newptr);
+ return (lkpi_kvrealloc(ptr, oldsize, newsize, flags));
}
+#endif
/*
* Misc.
@@ -273,12 +308,6 @@ kfree_sensitive(const void *ptr)
}
static inline size_t
-ksize(const void *ptr)
-{
- return (malloc_usable_size(ptr));
-}
-
-static inline size_t
kmalloc_size_roundup(size_t size)
{
if (unlikely(size == 0 || size == SIZE_MAX))
diff --git a/sys/compat/linuxkpi/common/include/linux/soc/airoha/airoha_offload.h b/sys/compat/linuxkpi/common/include/linux/soc/airoha/airoha_offload.h
new file mode 100644
index 000000000000..ade0b06d839f
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/soc/airoha/airoha_offload.h
@@ -0,0 +1,48 @@
+/*-
+ * Copyright (c) 2026 Bjoern A. Zeeb
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef _LINUXKPI_LINUX_SOC_AIROHA_AIROHA_OFFLOAD_H
+#define _LINUXKPI_LINUX_SOC_AIROHA_AIROHA_OFFLOAD_H
+
+#include <linux/kernel.h> /* pr_debug */
+
+enum airoha_npu_wlan_get_cmd {
+ __dummy_airoha_npu_wlan_get_cmd,
+};
+enum airoha_npu_wlan_set_cmd {
+ __dummy_airoha_npu_wlan_set_cmd,
+};
+
+struct airoha_npu {
+};
+struct airoha_npu_rx_dma_desc {
+};
+struct airoha_npu_tx_dma_desc {
+};
+
+static __inline int
+airoha_npu_wlan_send_msg(void *npu, int ifindex,
+ enum airoha_npu_wlan_set_cmd cmd, void *val, size_t len, gfp_t gfp)
+{
+ pr_debug("%s: TODO\n", __func__);
+ return (-EOPNOTSUPP);
+}
+
+static __inline int
+airoha_npu_wlan_get_msg(void *npu, int ifindex,
+ enum airoha_npu_wlan_get_cmd cmd, void *val, size_t len, gfp_t gfp)
+{
+ pr_debug("%s: TODO\n", __func__);
+ return (-EOPNOTSUPP);
+}
+
+static __inline void
+airoha_npu_wlan_enable_irq(struct airoha_npu *npu, int q)
+{
+ pr_debug("%s: TODO\n", __func__);
+}
+
+#endif /* _LINUXKPI_LINUX_SOC_AIROHA_AIROHA_OFFLOAD_H */
diff --git a/sys/compat/linuxkpi/common/include/linux/soc/mediatek/mtk_wed.h b/sys/compat/linuxkpi/common/include/linux/soc/mediatek/mtk_wed.h
index 2b9c6ae4911e..64daa8c78c9d 100644
--- a/sys/compat/linuxkpi/common/include/linux/soc/mediatek/mtk_wed.h
+++ b/sys/compat/linuxkpi/common/include/linux/soc/mediatek/mtk_wed.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2022-2025 Bjoern A. Zeeb
+ * Copyright (c) 2022-2026 Bjoern A. Zeeb
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@@ -37,7 +37,13 @@ mtk_wed_device_active(struct mtk_wed_device *dev __unused)
static inline bool
mtk_wed_get_rx_capa(struct mtk_wed_device *dev __unused)
{
+ pr_debug("%s: TODO\n", __func__);
+ return (false);
+}
+static inline bool
+mtk_wed_is_amsdu_supported(struct mtk_wed_device *dev __unused)
+{
pr_debug("%s: TODO\n", __func__);
return (false);
}
@@ -66,6 +72,12 @@ mtk_wed_get_rx_capa(struct mtk_wed_device *dev __unused)
{
return (false);
}
+
+static inline bool
+mtk_wed_is_amsdu_supported(struct mtk_wed_device *dev __unused)
+{
+ return (false);
+}
#endif /* CONFIG_NET_MEDIATEK_SOC_WED */
#endif /* _LINUXKPI_LINUX_SOC_MEDIATEK_MTK_WED_H */
diff --git a/sys/compat/linuxkpi/common/include/linux/sort.h b/sys/compat/linuxkpi/common/include/linux/sort.h
index e6196d1f41c7..972c8514e6fb 100644
--- a/sys/compat/linuxkpi/common/include/linux/sort.h
+++ b/sys/compat/linuxkpi/common/include/linux/sort.h
@@ -34,8 +34,13 @@
#include <sys/libkern.h>
#define sort(base, num, size, cmp, swap) do { \
- BUILD_BUG_ON_ZERO(swap); \
+ BUILD_BUG_ON(swap); \
qsort(base, num, size, cmp); \
} while (0)
+#define sort_r(base, num, size, cmp, swap, priv) do { \
+ BUILD_BUG_ON(swap); \
+ qsort_r(base, num, size, cmp, priv); \
+} while (0)
+
#endif /* _LINUXKPI_LINUX_SORT_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/spinlock.h b/sys/compat/linuxkpi/common/include/linux/spinlock.h
index 341e89b6feed..a786cbab5e13 100644
--- a/sys/compat/linuxkpi/common/include/linux/spinlock.h
+++ b/sys/compat/linuxkpi/common/include/linux/spinlock.h
@@ -36,10 +36,12 @@
#include <sys/mutex.h>
#include <sys/kdb.h>
+#include <linux/cleanup.h>
#include <linux/compiler.h>
#include <linux/rwlock.h>
#include <linux/bottom_half.h>
#include <linux/lockdep.h>
+#include <linux/preempt.h>
typedef struct mtx spinlock_t;
@@ -198,4 +200,13 @@ typedef struct raw_spinlock {
#define raw_spin_trylock_irqsave(rl, f) spin_trylock_irqsave(&(rl)->lock, (f))
#define raw_spin_unlock_irqrestore(rl, f) spin_unlock_irqrestore(&(rl)->lock, (f))
+/*
+ * cleanup.h related pre-defined cases.
+ */
+DEFINE_LOCK_GUARD_1(spinlock_irqsave,
+ spinlock_t,
+ spin_lock_irqsave(_T->lock, _T->flags),
+ spin_unlock_irqrestore(_T->lock, _T->flags),
+ unsigned long flags)
+
#endif /* _LINUXKPI_LINUX_SPINLOCK_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/ssb/ssb_regs.h b/sys/compat/linuxkpi/common/include/linux/ssb/ssb_regs.h
new file mode 100644
index 000000000000..e1c18b6b632a
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/ssb/ssb_regs.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2025 Bjoern A. Zeeb
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef _LINUXKPI_LINUX_SSB_SSB_REGS_H
+#define _LINUXKPI_LINUX_SSB_SSB_REGS_H
+
+#define SSB_IDHIGH_RCHI 0x00007000
+#define SSB_IDHIGH_RCHI_SHIFT 8
+#define SSB_IDHIGH_RCLO 0x0000000F
+#define SSB_IDLOW_INITIATOR 0x00000080
+#define SSB_IMSTATE_BUSY 0x01800000
+#define SSB_IMSTATE_IBE 0x00020000
+#define SSB_IMSTATE_REJECT 0x02000000
+#define SSB_IMSTATE_TO 0x00040000
+#define SSB_TMSHIGH_BUSY 0x00000004
+#define SSB_TMSHIGH_SERR 0x00000001
+#define SSB_TMSLOW_CLOCK 0x00010000
+#define SSB_TMSLOW_FGC 0x00020000
+#define SSB_TMSLOW_REJECT 0x00000002
+#define SSB_TMSLOW_RESET 0x00000001
+
+#endif /* _LINUXKPI_LINUX_SSB_SSB_REGS_H */
diff --git a/sys/compat/linuxkpi/common/include/linux/string.h b/sys/compat/linuxkpi/common/include/linux/string.h
index f7b64560d254..b195dcc8fe9b 100644
--- a/sys/compat/linuxkpi/common/include/linux/string.h
+++ b/sys/compat/linuxkpi/common/include/linux/string.h
@@ -31,6 +31,7 @@
#include <sys/ctype.h>
+#include <linux/array_size.h>
#include <linux/types.h>
#include <linux/gfp.h>
#include <linux/slab.h>
@@ -88,6 +89,17 @@ memdup_user_nul(const void *ptr, size_t len)
}
static inline void *
+memdup_array_user(const void *src, size_t n, size_t size)
+{
+ size_t len;
+
+ if (check_mul_overflow(n, size, &len))
+ return (ERR_PTR(-EOVERFLOW));
+
+ return (memdup_user(src, len));
+}
+
+static inline void *
kmemdup(const void *src, size_t len, gfp_t gfp)
{
void *dst;
@@ -98,6 +110,12 @@ kmemdup(const void *src, size_t len, gfp_t gfp)
return (dst);
}
+static inline void *
+kmemdup_array(const void *src, size_t count, size_t element_size, gfp_t gfp)
+{
+ return (kmemdup(src, size_mul(count, element_size), gfp));
+}
+
/* See slab.h for kvmalloc/kvfree(). */
static inline void *
kvmemdup(const void *src, size_t len, gfp_t gfp)
@@ -198,6 +216,12 @@ memchr_inv(const void *start, int c, size_t length)
return (NULL);
}
+static inline bool
+mem_is_zero(const void *start, size_t length)
+{
+ return (memchr_inv(start, 0, length) == NULL);
+}
+
static inline size_t
str_has_prefix(const char *str, const char *prefix)
{
@@ -303,6 +327,22 @@ memcpy_and_pad(void *dst, size_t dstlen, const void *src, size_t len, int ch)
}
}
+#define strtomem(dst, src) do { \
+ size_t dstlen = ARRAY_SIZE(dst); \
+ size_t srclen = __builtin_object_size(src, 1); \
+ srclen = MIN(srclen, dstlen); \
+ srclen = strnlen(src, srclen); \
+ memcpy(dst, src, srclen); \
+} while (0)
+
+#define strtomem_pad(dst, src, pad) do { \
+ size_t dstlen = ARRAY_SIZE(dst); \
+ size_t srclen = __builtin_object_size(src, 1); \
+ srclen = MIN(srclen, dstlen); \
+ srclen = strnlen(src, srclen); \
+ memcpy_and_pad(dst, dstlen, src, srclen, pad); \
+} while (0)
+
#define memset_startat(ptr, bytepat, smember) \
({ \
uint8_t *_ptr = (uint8_t *)(ptr); \
diff --git a/sys/compat/linuxkpi/common/include/linux/string_choices.h b/sys/compat/linuxkpi/common/include/linux/string_choices.h
index 74aa3fd019b2..db540d3e7d40 100644
--- a/sys/compat/linuxkpi/common/include/linux/string_choices.h
+++ b/sys/compat/linuxkpi/common/include/linux/string_choices.h
@@ -33,39 +33,33 @@
static inline const char *
str_yes_no(bool value)
{
- if (value)
- return "yes";
- else
- return "no";
+ return (value ? "yes" : "no");
}
static inline const char *
str_on_off(bool value)
{
- if (value)
- return "on";
- else
- return "off";
+ return (value ? "on" : "off");
}
static inline const char *
str_enabled_disabled(bool value)
{
- if (value)
- return "enabled";
- else
- return "disabled";
+ return (value ? "enabled" : "disabled");
}
static inline const char *
str_enable_disable(bool value)
{
- if (value)
- return "enable";
- else
- return "disable";
+ return (value ? "enable" : "disable");
}
#define str_disable_enable(_v) str_enable_disable(!(_v))
+static inline const char *
+str_read_write(bool value)
+{
+ return (value ? "read" : "write");
+}
+
#endif
diff --git a/sys/compat/linuxkpi/common/include/linux/suspend.h b/sys/compat/linuxkpi/common/include/linux/suspend.h
index 3d5d5d594127..3a8bec392437 100644
--- a/sys/compat/linuxkpi/common/include/linux/suspend.h
+++ b/sys/compat/linuxkpi/common/include/linux/suspend.h
@@ -20,10 +20,29 @@ extern suspend_state_t pm_suspend_target_state;
#define PM_SUSPEND_MIN PM_SUSPEND_TO_IDLE
#define PM_SUSPEND_MAX 4
+#define PM_HIBERNATION_PREPARE 0x0001
+#define PM_POST_HIBERNATION 0x0002
+#define PM_SUSPEND_PREPARE 0x0003
+#define PM_POST_SUSPEND 0x0004
+#define PM_RESTORE_PREPARE 0x0005
+#define PM_POST_RESTORE 0x0006
+
static inline int
pm_suspend_via_firmware(void)
{
- return 0;
+ return (0);
+}
+
+static inline int
+register_pm_notifier(struct notifier_block *nb)
+{
+ return (0);
+}
+
+static inline int
+unregister_pm_notifier(struct notifier_block *nb)
+{
+ return (0);
}
#endif /* _LINUXKPI_LINUX_SUSPEND_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/sysfs.h b/sys/compat/linuxkpi/common/include/linux/sysfs.h
index 470c224a9778..7c8c4e2e32b9 100644
--- a/sys/compat/linuxkpi/common/include/linux/sysfs.h
+++ b/sys/compat/linuxkpi/common/include/linux/sysfs.h
@@ -43,13 +43,6 @@ struct sysfs_ops {
size_t);
};
-struct attribute_group {
- const char *name;
- mode_t (*is_visible)(struct kobject *,
- struct attribute *, int);
- struct attribute **attrs;
-};
-
struct bin_attribute {
struct attribute attr;
size_t size;
@@ -59,6 +52,14 @@ struct bin_attribute {
struct bin_attribute *, char *, loff_t, size_t);
};
+struct attribute_group {
+ const char *name;
+ mode_t (*is_visible)(struct kobject *,
+ struct attribute *, int);
+ struct attribute **attrs;
+ struct bin_attribute **bin_attrs;
+};
+
#define __ATTR(_name, _mode, _show, _store) { \
.attr = { .name = __stringify(_name), .mode = _mode }, \
.show = _show, .store = _store, \
@@ -370,6 +371,7 @@ static inline int
sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp)
{
struct attribute **attr;
+ struct bin_attribute **bin_attr;
struct sysctl_oid *oidp;
/* Don't create the group node if grp->name is undefined. */
@@ -378,11 +380,19 @@ sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp)
OID_AUTO, grp->name, CTLFLAG_RD|CTLFLAG_MPSAFE, NULL, grp->name);
else
oidp = kobj->oidp;
- for (attr = grp->attrs; *attr != NULL; attr++) {
+ for (attr = grp->attrs; attr != NULL && *attr != NULL; attr++) {
SYSCTL_ADD_OID(NULL, SYSCTL_CHILDREN(oidp), OID_AUTO,
(*attr)->name, CTLTYPE_STRING|CTLFLAG_RW|CTLFLAG_MPSAFE,
kobj, (uintptr_t)*attr, sysctl_handle_attr, "A", "");
}
+ for (bin_attr = grp->bin_attrs;
+ bin_attr != NULL && *bin_attr != NULL;
+ bin_attr++) {
+ SYSCTL_ADD_OID(NULL, SYSCTL_CHILDREN(oidp), OID_AUTO,
+ (*bin_attr)->attr.name,
+ CTLTYPE_OPAQUE|CTLFLAG_RW|CTLFLAG_MPSAFE,
+ kobj, (uintptr_t)*bin_attr, sysctl_handle_bin_attr, "", "");
+ }
return (0);
}
@@ -434,14 +444,20 @@ static inline void
sysfs_unmerge_group(struct kobject *kobj, const struct attribute_group *grp)
{
struct attribute **attr;
+ struct bin_attribute **bin_attr;
struct sysctl_oid *oidp;
SYSCTL_FOREACH(oidp, SYSCTL_CHILDREN(kobj->oidp)) {
if (strcmp(oidp->oid_name, grp->name) != 0)
continue;
- for (attr = grp->attrs; *attr != NULL; attr++) {
+ for (attr = grp->attrs; attr != NULL && *attr != NULL; attr++) {
sysctl_remove_name(oidp, (*attr)->name, 1, 1);
}
+ for (bin_attr = grp->bin_attrs;
+ bin_attr != NULL && *bin_attr != NULL;
+ bin_attr++) {
+ sysctl_remove_name(oidp, (*bin_attr)->attr.name, 1, 1);
+ }
}
}
diff --git a/sys/compat/linuxkpi/common/include/linux/unaligned.h b/sys/compat/linuxkpi/common/include/linux/unaligned.h
new file mode 100644
index 000000000000..5a59a4b36b4f
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/unaligned.h
@@ -0,0 +1,12 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 The FreeBSD Foundation
+ */
+
+#ifndef _LINUXKPI_LINUX_UNALIGNED_H
+#define _LINUXKPI_LINUX_UNALIGNED_H
+
+#include <asm/unaligned.h>
+
+#endif /* _LINUXKPI_LINUX_UNALIGNED_H */
diff --git a/sys/compat/linuxkpi/common/include/linux/uuid.h b/sys/compat/linuxkpi/common/include/linux/uuid.h
index 4f6f4a8b34f0..0765ef4dcc1e 100644
--- a/sys/compat/linuxkpi/common/include/linux/uuid.h
+++ b/sys/compat/linuxkpi/common/include/linux/uuid.h
@@ -33,6 +33,7 @@
#include <linux/random.h>
+#define UUID_SIZE 16
#define UUID_STRING_LEN 36
#define GUID_INIT(x0_3, x4_5, x6_7, x8, x9, x10, x11, x12, x13, x14, x15) \
@@ -59,6 +60,8 @@ typedef struct {
char x[16];
} guid_t;
+extern const guid_t guid_null;
+
static inline void
guid_gen(guid_t *g)
{
@@ -68,10 +71,34 @@ guid_gen(guid_t *g)
g->x[8] = (g->x[8] & 0x3f) | 0x80;
}
+static inline bool
+guid_equal(const guid_t *u1, const guid_t *u2)
+{
+ return (memcmp(u1, u2, sizeof(guid_t)) == 0);
+}
+
static inline void
guid_copy(guid_t *dst, const guid_t *src)
{
memcpy(dst, src, sizeof(*dst));
}
+static inline void
+import_guid(guid_t *dst, const uint8_t *src)
+{
+ memcpy(dst, src, sizeof(guid_t));
+}
+
+static inline void
+export_guid(uint8_t *dst, const guid_t *src)
+{
+ memcpy(dst, src, sizeof(guid_t));
+}
+
+static inline bool
+guid_is_null(const guid_t *guid)
+{
+ return (guid_equal(guid, &guid_null));
+}
+
#endif /* _LINUXKPI_LINUX_UUID_H */
diff --git a/sys/compat/linuxkpi/common/include/linux/wait.h b/sys/compat/linuxkpi/common/include/linux/wait.h
index 03ddce2c06f5..698a1056b713 100644
--- a/sys/compat/linuxkpi/common/include/linux/wait.h
+++ b/sys/compat/linuxkpi/common/include/linux/wait.h
@@ -43,13 +43,6 @@
#define SKIP_SLEEP() (SCHEDULER_STOPPED() || kdb_active)
-#define might_sleep() \
- WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "might_sleep()")
-
-#define might_sleep_if(cond) do { \
- if (cond) { might_sleep(); } \
-} while (0)
-
struct wait_queue;
struct wait_queue_head;
diff --git a/sys/compat/linuxkpi/common/include/linux/wordpart.h b/sys/compat/linuxkpi/common/include/linux/wordpart.h
new file mode 100644
index 000000000000..ce697bbef3b3
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/wordpart.h
@@ -0,0 +1,13 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 Jean-Sébastien Pédron
+ */
+
+#ifndef _LINUXKPI_LINUX_WORDPART_H_
+#define _LINUXKPI_LINUX_WORDPART_H_
+
+#define lower_32_bits(n) ((u32)(n))
+#define upper_32_bits(n) ((u32)(((n) >> 16) >> 16))
+
+#endif /* _LINUXKPI_LINUX_WORDPART_H_ */
diff --git a/sys/compat/linuxkpi/common/include/net/cfg80211.h b/sys/compat/linuxkpi/common/include/net/cfg80211.h
index 4b21f82a0762..dedf687d6530 100644
--- a/sys/compat/linuxkpi/common/include/net/cfg80211.h
+++ b/sys/compat/linuxkpi/common/include/net/cfg80211.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2020-2025 The FreeBSD Foundation
+ * Copyright (c) 2020-2026 The FreeBSD Foundation
* Copyright (c) 2021-2022 Bjoern A. Zeeb
*
* This software was developed by Björn Zeeb under sponsorship from
@@ -124,6 +124,7 @@ enum ieee80211_channel_flags {
IEEE80211_CHAN_PSD = BIT(12),
IEEE80211_CHAN_ALLOW_6GHZ_VLP_AP = BIT(13),
IEEE80211_CHAN_CAN_MONITOR = BIT(14),
+ IEEE80211_CHAN_NO_EHT = BIT(15),
};
#define IEEE80211_CHAN_NO_HT40 (IEEE80211_CHAN_NO_HT40MINUS|IEEE80211_CHAN_NO_HT40PLUS)
@@ -152,6 +153,8 @@ struct linuxkpi_ieee80211_channel {
int orig_mpwr;
};
+#define NL80211_EHT_NSS_MAX 16
+
struct cfg80211_bitrate_mask {
/* TODO FIXME */
struct {
@@ -159,9 +162,12 @@ struct cfg80211_bitrate_mask {
uint8_t ht_mcs[IEEE80211_HT_MCS_MASK_LEN];
uint16_t vht_mcs[8];
uint16_t he_mcs[8];
+ uint16_t eht_mcs[NL80211_EHT_NSS_MAX];
enum nl80211_txrate_gi gi;
enum nl80211_he_gi he_gi;
- uint8_t he_ltf; /* XXX enum? */
+ enum nl80211_he_ltf he_ltf;
+ enum nl80211_eht_gi eht_gi;
+ uint8_t eht_ltf; /* XXX enum? */
} control[NUM_NL80211_BANDS];
};
@@ -324,6 +330,53 @@ struct cfg80211_chan_def {
uint16_t punctured;
};
+struct cfg80211_nan_band_config {
+ /* XXX TODO */
+ struct linuxkpi_ieee80211_channel *chan;
+ uint8_t rssi_middle;
+ uint8_t rssi_close;
+ uint8_t awake_dw_interval;
+};
+
+struct cfg80211_nan_conf {
+ uint8_t bands;
+ uint8_t discovery_beacon_interval;
+ uint8_t master_pref;
+ bool enable_dw_notification;
+
+ uint16_t scan_dwell_time;
+ uint16_t scan_period;
+ uint16_t extra_nan_attrs_len;
+ uint16_t vendor_elems_len;
+
+ uint8_t *cluster_id;
+ struct cfg80211_nan_band_config band_cfgs[NUM_NL80211_BANDS];
+ uint8_t *extra_nan_attrs;
+ uint8_t *vendor_elems;
+};
+
+enum wiphy_nan_flags {
+ WIPHY_NAN_FLAGS_CONFIGURABLE_SYNC = BIT(0),
+ WIPHY_NAN_FLAGS_USERSPACE_DE = BIT(1),
+};
+
+/* Wi-Fi Aware (TM) specification, 9.5.15 Device Capability attribute. */
+/* Misplaced here for the moment. */
+#define NAN_OP_MODE_PHY_MODE_MASK 0x11 /* b0 0=HT, 1=VHT, b4=1 HE supported */
+#define NAN_OP_MODE_80P80MHZ 0x2 /* b1 */
+#define NAN_OP_MODE_160MHZ 0x4 /* b2 */
+
+#define NAN_DEV_CAPA_EXT_KEY_ID_SUPPORTED 0x2 /* b1 */
+#define NAN_DEV_CAPA_NDPE_SUPPORTED 0x8 /* b3 */
+
+struct wiphy_nan_capa {
+ uint32_t flags; /* enum wiphy_nan_flags */
+ uint8_t op_mode;
+ uint8_t dev_capabilities;
+ uint8_t n_antennas; /* Tx/Rx bitmask, e.g., 0x22 */
+ uint16_t max_channel_switch_time;
+};
+
struct cfg80211_ftm_responder_stats {
/* XXX TODO */
int asap_num, failed_num, filled, non_asap_num, out_of_window_triggers_num, partial_num, reschedule_requests_num, success_num, total_duration_ms, unknown_triggers_num;
@@ -1017,6 +1070,14 @@ struct survey_info { /* net80211::struct ieee80211_channel_survey */
struct linuxkpi_ieee80211_channel *channel;
};
+enum wiphy_bss_param_flags {
+ WIPHY_BSS_PARAM_AP_ISOLATE = BIT(0),
+};
+
+struct bss_parameters {
+ int ap_isolate;
+};
+
enum wiphy_vendor_cmd_need_flags {
WIPHY_VENDOR_CMD_NEED_NETDEV = 0x01,
WIPHY_VENDOR_CMD_NEED_RUNNING = 0x02,
@@ -1138,6 +1199,11 @@ struct wiphy {
int n_radio;
const struct wiphy_radio *radio;
+ uint32_t bss_param_support; /* enum wiphy_bss_param_flags */
+
+ uint8_t nan_supported_bands;
+ struct wiphy_nan_capa nan_capa;
+
int features, hw_version;
int interface_modes, max_match_sets, max_remain_on_channel_duration, max_scan_ssids, max_sched_scan_ie_len, max_sched_scan_plan_interval, max_sched_scan_plan_iterations, max_sched_scan_plans, max_sched_scan_reqs, max_sched_scan_ssids;
int num_iftype_ext_capab;
@@ -1220,7 +1286,7 @@ struct cfg80211_ops {
int (*dump_survey)(struct wiphy *, struct net_device *, int, struct survey_info *);
int (*external_auth)(struct wiphy *, struct net_device *, struct cfg80211_external_auth_params *);
int (*set_cqm_rssi_range_config)(struct wiphy *, struct net_device *, int, int);
-
+ int (*change_bss)(struct wiphy *, struct net_device *, struct bss_parameters *);
};
@@ -1295,8 +1361,11 @@ wiphy_dev(struct wiphy *wiphy)
return (wiphy->dev);
}
-#define wiphy_dereference(_w, p) \
- rcu_dereference_check(p, lockdep_is_held(&(_w)->mtx))
+#define wiphy_dereference(_w, _p) \
+ rcu_dereference_protected(_p, lockdep_is_held(&(_w)->mtx))
+
+#define rcu_dereference_wiphy(_w, _p) \
+ rcu_dereference_check(_p, lockdep_is_held(&(_w)->mtx))
#define wiphy_lock(_w) mutex_lock(&(_w)->mtx)
#define wiphy_unlock(_w) mutex_unlock(&(_w)->mtx)
@@ -2194,6 +2263,22 @@ cfg80211_cqm_rssi_notify(struct net_device *dev,
/* -------------------------------------------------------------------------- */
static inline void
+cfg80211_nan_cluster_joined(struct wireless_dev *wdev, const uint8_t *cluster_id,
+ bool new_cluster, gfp_t gfp)
+{
+ TODO("NAN");
+}
+
+static inline void
+cfg80211_next_nan_dw_notif(struct wireless_dev *wdev,
+ struct linuxkpi_ieee80211_channel *chan, gfp_t gfp)
+{
+ TODO("NAN");
+}
+
+/* -------------------------------------------------------------------------- */
+
+static inline void
wiphy_work_init(struct wiphy_work *wwk, wiphy_work_fn fn)
{
INIT_LIST_HEAD(&wwk->entry);
diff --git a/sys/compat/linuxkpi/common/include/net/mac80211.h b/sys/compat/linuxkpi/common/include/net/mac80211.h
index 6e2f3f2d8781..3c783d1f3c8a 100644
--- a/sys/compat/linuxkpi/common/include/net/mac80211.h
+++ b/sys/compat/linuxkpi/common/include/net/mac80211.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2020-2025 The FreeBSD Foundation
+ * Copyright (c) 2020-2026 The FreeBSD Foundation
* Copyright (c) 2020-2025 Bjoern A. Zeeb
*
* This software was developed by Björn Zeeb under sponsorship from
@@ -431,7 +431,6 @@ enum ieee80211_hw_flags {
IEEE80211_HW_BUFF_MMPDU_TXQ,
IEEE80211_HW_CHANCTX_STA_CSA,
IEEE80211_HW_CONNECTION_MONITOR,
- IEEE80211_HW_DEAUTH_NEED_MGD_TX_PREP,
IEEE80211_HW_HAS_RATE_CONTROL,
IEEE80211_HW_MFP_CAPABLE,
IEEE80211_HW_NEEDS_UNIQUE_STA_ADDR,
@@ -604,7 +603,7 @@ enum ieee80211_rx_status_flags {
RX_FLAG_AMPDU_IS_LAST = BIT(21),
RX_FLAG_AMPDU_LAST_KNOWN = BIT(22),
RX_FLAG_AMSDU_MORE = BIT(23),
- /* = BIT(24), */
+ RX_FLAG_RADIOTAP_VHT = BIT(24),
RX_FLAG_ONLY_MONITOR = BIT(25),
RX_FLAG_SKIP_MONITOR = BIT(26),
RX_FLAG_8023 = BIT(27),
@@ -789,10 +788,21 @@ struct ieee80211_tx_queue_params {
struct ieee80211_he_mu_edca_param_ac_rec mu_edca_param_rec;
};
+enum mac80211_rate_control_flags {
+ IEEE80211_TX_RC_40_MHZ_WIDTH = BIT(0),
+ IEEE80211_TX_RC_80_MHZ_WIDTH = BIT(1),
+ IEEE80211_TX_RC_160_MHZ_WIDTH = BIT(2),
+ IEEE80211_TX_RC_GREEN_FIELD = BIT(3),
+ IEEE80211_TX_RC_MCS = BIT(4),
+ IEEE80211_TX_RC_SHORT_GI = BIT(5),
+ IEEE80211_TX_RC_VHT_MCS = BIT(6),
+ IEEE80211_TX_RC_USE_SHORT_PREAMBLE = BIT(7),
+};
+
struct ieee80211_tx_rate {
uint8_t idx;
uint16_t count:5,
- flags:11;
+ flags:11; /* enum mac80211_rate_control_flags */
};
enum ieee80211_vif_driver_flags {
@@ -1092,6 +1102,12 @@ struct ieee80211_ops {
void (*rfkill_poll)(struct ieee80211_hw *);
+ int (*net_fill_forward_path)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *, struct net_device_path_ctx *, struct net_device_path *);
+
+ int (*start_nan)(struct ieee80211_hw *, struct ieee80211_vif *, struct cfg80211_nan_conf *);
+ int (*stop_nan)(struct ieee80211_hw *, struct ieee80211_vif *);
+ int (*nan_change_conf)(struct ieee80211_hw *, struct ieee80211_vif *, struct cfg80211_nan_conf *, uint32_t changes);
+
/* #ifdef CONFIG_MAC80211_DEBUGFS */ /* Do not change depending on compile-time option. */
void (*sta_add_debugfs)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *, struct dentry *);
void (*vif_add_debugfs)(struct ieee80211_hw *, struct ieee80211_vif *);
@@ -1184,6 +1200,22 @@ void linuxkpi_ieee80211_handle_wake_tx_queue(struct ieee80211_hw *,
/* -------------------------------------------------------------------------- */
+/*
+ * Emulate chanctx operations. We cannot rename/prefix the functions
+ * as we rely on the (function)pointers being the same everywhere.
+ */
+int ieee80211_emulate_add_chanctx(struct ieee80211_hw *,
+ struct ieee80211_chanctx_conf *);
+void ieee80211_emulate_remove_chanctx(struct ieee80211_hw *,
+ struct ieee80211_chanctx_conf *);
+void ieee80211_emulate_change_chanctx(struct ieee80211_hw *,
+ struct ieee80211_chanctx_conf *, uint32_t);
+int ieee80211_emulate_switch_vif_chanctx(struct ieee80211_hw *,
+ struct ieee80211_vif_chanctx_switch *, int,
+ enum ieee80211_chanctx_switch_mode);
+
+/* -------------------------------------------------------------------------- */
+
static __inline void
_ieee80211_hw_set(struct ieee80211_hw *hw, enum ieee80211_hw_flags flag)
{
@@ -1549,6 +1581,16 @@ ieee80211_iterate_stations_atomic(struct ieee80211_hw *hw,
linuxkpi_ieee80211_iterate_stations_atomic(hw, iterfunc, arg);
}
+static inline void
+ieee80211_iterate_stations_mtx(struct ieee80211_hw *hw,
+ void (*iterfunc)(void *, struct ieee80211_sta *), void *arg)
+{
+
+ lockdep_assert_wiphy(hw->wiphy);
+ IMPROVE("we could simplify this if we had a sta list on the lhw");
+ linuxkpi_ieee80211_iterate_stations_atomic(hw, iterfunc, arg);
+}
+
static __inline struct wireless_dev *
ieee80211_vif_to_wdev(struct ieee80211_vif *vif)
{
@@ -2622,60 +2664,11 @@ ieee80211_cqm_beacon_loss_notify(struct ieee80211_vif *vif, gfp_t gfp __unused)
/* -------------------------------------------------------------------------- */
-int lkpi_80211_update_chandef(struct ieee80211_hw *,
- struct ieee80211_chanctx_conf *);
-
-static inline int
-ieee80211_emulate_add_chanctx(struct ieee80211_hw *hw,
- struct ieee80211_chanctx_conf *chanctx_conf)
-{
- int error;
-
- hw->conf.radar_enabled = chanctx_conf->radar_enabled;
- error = lkpi_80211_update_chandef(hw, chanctx_conf);
- return (error);
-}
-
-static inline void
-ieee80211_emulate_remove_chanctx(struct ieee80211_hw *hw,
- struct ieee80211_chanctx_conf *chanctx_conf __unused)
-{
- hw->conf.radar_enabled = false;
- lkpi_80211_update_chandef(hw, NULL);
-}
-
-static inline void
-ieee80211_emulate_change_chanctx(struct ieee80211_hw *hw,
- struct ieee80211_chanctx_conf *chanctx_conf, uint32_t changed __unused)
-{
- hw->conf.radar_enabled = chanctx_conf->radar_enabled;
- lkpi_80211_update_chandef(hw, chanctx_conf);
-}
-
-static inline int
-ieee80211_emulate_switch_vif_chanctx(struct ieee80211_hw *hw,
- struct ieee80211_vif_chanctx_switch *vifs, int n_vifs,
- enum ieee80211_chanctx_switch_mode mode __unused)
+static inline bool
+ieee80211_vif_nan_started(struct ieee80211_vif *vif)
{
- struct ieee80211_chanctx_conf *chanctx_conf;
- int error;
-
- /* Sanity check. */
- if (n_vifs <= 0)
- return (-EINVAL);
- if (vifs == NULL || vifs[0].new_ctx == NULL)
- return (-EINVAL);
-
- /*
- * What to do if n_vifs > 1?
- * Does that make sense for drivers not supporting chanctx?
- */
- hw->conf.radar_enabled = vifs[0].new_ctx->radar_enabled;
- chanctx_conf = vifs[0].new_ctx;
- error = lkpi_80211_update_chandef(hw, chanctx_conf);
- return (error);
+ IMPROVE("NAN");
+ return (false);
}
-/* -------------------------------------------------------------------------- */
-
#endif /* _LINUXKPI_NET_MAC80211_H */
diff --git a/sys/compat/linuxkpi/common/src/linux_80211.c b/sys/compat/linuxkpi/common/src/linux_80211.c
index d25b32f1dae8..b9528295ad8e 100644
--- a/sys/compat/linuxkpi/common/src/linux_80211.c
+++ b/sys/compat/linuxkpi/common/src/linux_80211.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2020-2025 The FreeBSD Foundation
+ * Copyright (c) 2020-2026 The FreeBSD Foundation
* Copyright (c) 2020-2025 Bjoern A. Zeeb
*
* This software was developed by Björn Zeeb under sponsorship from
@@ -181,6 +181,8 @@ static void lkpi_ieee80211_free_skb_mbuf(void *);
#ifdef LKPI_80211_WME
static int lkpi_wme_update(struct lkpi_hw *, struct ieee80211vap *, bool);
#endif
+static int lkpi_80211_update_chandef(struct ieee80211_hw *,
+ struct ieee80211_chanctx_conf *);
static void lkpi_ieee80211_wake_queues_locked(struct ieee80211_hw *);
static const char *
@@ -766,6 +768,7 @@ lkpi_sta_sync_from_ni(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
lkpi_sync_chanctx_cw_from_rx_bw(hw, vif, sta);
}
+#if 0
static uint8_t
lkpi_get_max_rx_chains(struct ieee80211_node *ni)
{
@@ -792,6 +795,7 @@ lkpi_get_max_rx_chains(struct ieee80211_node *ni)
return (chains);
}
+#endif
static void
lkpi_lsta_dump(struct lkpi_sta *lsta, struct ieee80211_node *ni,
@@ -1373,6 +1377,15 @@ lkpi_sta_del_keys(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
return (0);
lockdep_assert_wiphy(hw->wiphy);
+
+ if (vif->cfg.assoc && lsta->state == IEEE80211_STA_AUTHORIZED) {
+ if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO)
+ ic_printf(lsta->ni->ni_ic,
+ "%d %lu %s: vif still assoc; not deleting keys\n",
+ curthread->td_tid, jiffies, __func__);
+ return (0);
+ }
+
ieee80211_ref_node(lsta->ni);
error = 0;
@@ -1452,6 +1465,15 @@ lkpi_iv_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k)
*/
lockdep_assert_wiphy(hw->wiphy);
+ ni = ieee80211_ref_node(vap->iv_bss);
+ lsta = ni->ni_drv_data;
+ if (lsta == NULL) {
+ ic_printf(ic, "%s: ni %p (%6D) with lsta NULL\n",
+ __func__, ni, ni->ni_bssid, ":");
+ ieee80211_free_node(ni);
+ return (0);
+ }
+
/*
* While we are assoc we may still send packets. We cannot delete the
* keys as otherwise packets could go out unencrypted. Some firmware
@@ -1462,30 +1484,24 @@ lkpi_iv_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k)
* How to test: run 800Mbit/s UDP traffic and during that restart your
* supplicant. You want to survive that.
*/
- if (vif->cfg.assoc) {
+ if (vif->cfg.assoc && lsta->state == IEEE80211_STA_AUTHORIZED) {
if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO)
ic_printf(ic, "%d %lu %s: vif still assoc; not deleting keys\n",
curthread->td_tid, jiffies, __func__);
+ ieee80211_free_node(ni);
return (0);
}
if (IEEE80211_KEY_UNDEFINED(k)) {
ic_printf(ic, "%s: vap %p key %p is undefined: %p %u\n",
__func__, vap, k, k->wk_cipher, k->wk_keyix);
+ ieee80211_free_node(ni);
return (0);
}
if (vap->iv_bss == NULL) {
ic_printf(ic, "%s: iv_bss %p for vap %p is NULL\n",
__func__, vap->iv_bss, vap);
- return (0);
- }
-
- ni = ieee80211_ref_node(vap->iv_bss);
- lsta = ni->ni_drv_data;
- if (lsta == NULL) {
- ic_printf(ic, "%s: ni %p (%6D) with lsta NULL\n",
- __func__, ni, ni->ni_bssid, ":");
ieee80211_free_node(ni);
return (0);
}
@@ -1992,17 +2008,28 @@ lkpi_update_dtim_tsf(struct ieee80211_vif *vif, struct ieee80211_node *ni,
* we set the BSS_CHANGED_BEACON_INFO on the non-teardown
* path so make sure we only do run this check once we are
* assoc. (*iv_recv_mgmt)() will be called before we enter
- * here so the ni will be updates with information from the
+ * here so the ni will be updated with information from the
* beacon via net80211::sta_recv_mgmt(). We also need to
* make sure we do not do it on every beacon we still may
* get so only do if something changed. vif->bss_conf.dtim_period
* should be 0 as we start up (we also reset it on teardown).
+ *
+ * If we are assoc we need to make sure dtim_period is non-0.
+ * 0 is a reserved value and drivers assume they can DIV by it.
+ * In theory this means we need to wait for the first beacon
+ * before we finalize the vif being assoc. In practise that
+ * is harder until net80211 learns how to. Work around like
+ * this for the moment.
*/
- if (vif->cfg.assoc &&
- vif->bss_conf.dtim_period != ni->ni_dtim_period &&
- ni->ni_dtim_period > 0) {
- vif->bss_conf.dtim_period = ni->ni_dtim_period;
- bss_changed |= BSS_CHANGED_BEACON_INFO;
+ if (vif->cfg.assoc) {
+ if (vif->bss_conf.dtim_period != ni->ni_dtim_period &&
+ ni->ni_dtim_period > 0) {
+ vif->bss_conf.dtim_period = ni->ni_dtim_period;
+ bss_changed |= BSS_CHANGED_BEACON_INFO;
+ } else if (vif->bss_conf.dtim_period == 0) {
+ vif->bss_conf.dtim_period = 1;
+ bss_changed |= BSS_CHANGED_BEACON_INFO;
+ }
}
vif->bss_conf.sync_dtim_count = ni->ni_dtim_count;
@@ -2109,7 +2136,7 @@ lkpi_disassoc(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
* The caller is responsible for removing the sta gong to
* IEEE80211_STA_NOTEXIST and then executing the
* bss_info_changed() update.
- * See lkpi_sta_run_to_init() for more detailed comment.
+ * See DOWN4 for more detailed comment.
*/
lvif = VIF_TO_LVIF(vif);
@@ -2150,7 +2177,7 @@ lkpi_wake_tx_queues(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
if (no_emptyq && ltxq_empty)
continue;
- lkpi_80211_mo_wake_tx_queue(hw, sta->txq[tid]);
+ lkpi_80211_mo_wake_tx_queue(hw, sta->txq[tid], false);
}
}
@@ -2202,13 +2229,218 @@ lkpi_80211_flush_tx(struct lkpi_hw *lhw, struct lkpi_sta *lsta)
}
}
+static void
+lkpi_init_chandef(struct ieee80211com *ic __unused,
+ struct cfg80211_chan_def *chandef,
+ struct linuxkpi_ieee80211_channel *chan, struct ieee80211_channel *c,
+ bool can_ht)
+{
+
+ cfg80211_chandef_create(chandef, chan,
+ (can_ht) ? NL80211_CHAN_HT20 : NL80211_CHAN_NO_HT);
+ chandef->center_freq1 = ieee80211_get_channel_center_freq1(c);
+ chandef->center_freq2 = ieee80211_get_channel_center_freq2(c);
+
+ IMPROVE("Check ht/vht_cap from band not just chan? See lkpi_sta_sync_from_ni...");
+#ifdef LKPI_80211_HT
+ if (IEEE80211_IS_CHAN_HT(c)) {
+ if (IEEE80211_IS_CHAN_HT40(c))
+ chandef->width = NL80211_CHAN_WIDTH_40;
+ else
+ chandef->width = NL80211_CHAN_WIDTH_20;
+ }
+#endif
+#ifdef LKPI_80211_VHT
+ if (IEEE80211_IS_CHAN_VHT_5GHZ(c)) {
+ if (IEEE80211_IS_CHAN_VHT80P80(c))
+ chandef->width = NL80211_CHAN_WIDTH_80P80;
+ else if (IEEE80211_IS_CHAN_VHT160(c))
+ chandef->width = NL80211_CHAN_WIDTH_160;
+ else if (IEEE80211_IS_CHAN_VHT80(c))
+ chandef->width = NL80211_CHAN_WIDTH_80;
+ }
+#endif
+
+#ifdef LINUXKPI_DEBUG_80211
+ if ((linuxkpi_debug_80211 & D80211_CHANDEF) != 0)
+ ic_printf(ic, "%s:%d: chandef %p { chan %p { %u }, "
+ "width %d cfreq1 %u cfreq2 %u punctured %u }\n",
+ __func__, __LINE__, chandef,
+ chandef->chan, chandef->chan->center_freq,
+ chandef->width,
+ chandef->center_freq1, chandef->center_freq2,
+ chandef->punctured);
+#endif
+}
+
+static uint32_t
+lkpi_init_chanctx_conf(struct ieee80211_hw *hw,
+ struct cfg80211_chan_def *chandef,
+ struct ieee80211_chanctx_conf *chanctx_conf)
+{
+ uint32_t changed;
+
+ lockdep_assert_wiphy(hw->wiphy);
+
+ changed = 0;
+
+ chanctx_conf->rx_chains_static = 1;
+ chanctx_conf->rx_chains_dynamic = 1;
+ changed |= IEEE80211_CHANCTX_CHANGE_RX_CHAINS;
+
+ if (chanctx_conf->radar_enabled != hw->conf.radar_enabled) {
+ chanctx_conf->radar_enabled = hw->conf.radar_enabled;
+ changed |= IEEE80211_CHANCTX_CHANGE_RADAR;
+ }
+
+ chanctx_conf->def = *chandef;
+ changed |= IEEE80211_CHANCTX_CHANGE_WIDTH;
+
+ /* One day we should figure this out; is for iwlwifi-only. */
+ chanctx_conf->min_def = chanctx_conf->def;
+ changed |= IEEE80211_CHANCTX_CHANGE_MIN_WIDTH;
+
+ /* chanctx_conf->ap = */
+
+ return (changed);
+}
+
+static struct lkpi_chanctx *
+lkpi_alloc_lchanctx(struct ieee80211_hw *hw, struct lkpi_vif *lvif)
+{
+ struct lkpi_chanctx *lchanctx;
+
+ lchanctx = malloc(sizeof(*lchanctx) + hw->chanctx_data_size,
+ M_LKPI80211, M_WAITOK | M_ZERO);
+ lchanctx->lvif = lvif;
+
+ return (lchanctx);
+}
+
+static struct lkpi_chanctx *
+lkpi_find_lchanctx_reserved(struct ieee80211_hw *hw, struct lkpi_vif *lvif)
+{
+ struct lkpi_hw *lhw;
+ struct lkpi_chanctx *lchanctx;
+ bool found;
+
+ lhw = HW_TO_LHW(hw);
+
+ found = false;
+ rcu_read_lock();
+ list_for_each_entry_rcu(lchanctx, &lhw->lchanctx_list_reserved, entry) {
+ if (lchanctx->lvif == lvif) {
+ found = true;
+ break;
+ }
+ }
+ rcu_read_unlock();
+
+ if (!found) {
+ lchanctx = lkpi_alloc_lchanctx(hw, lvif);
+ list_add_rcu(&lchanctx->entry, &lhw->lchanctx_list_reserved);
+ }
+
+ return (lchanctx);
+}
+
+static struct ieee80211_chanctx_conf *
+lkpi_get_chanctx_conf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+ struct ieee80211_chanctx_conf *chanctx_conf;
+
+ chanctx_conf = rcu_dereference_protected(vif->bss_conf.chanctx_conf,
+ lockdep_is_held(&hw->wiphy->mtx));
+ if (chanctx_conf == NULL) {
+ struct lkpi_chanctx *lchanctx;
+ struct lkpi_vif *lvif;
+
+ lvif = VIF_TO_LVIF(vif);
+ lchanctx = lkpi_find_lchanctx_reserved(hw, lvif);
+ KASSERT(lchanctx != NULL, ("%s: hw %p, vif %p no lchanctx\n",
+ __func__, hw, vif));
+ list_del(&lchanctx->entry);
+ chanctx_conf = &lchanctx->chanctx_conf;
+ }
+ /* else { IMPROVE("diff changes for changed, working on live copy, rcu"); } */
+
+ return (chanctx_conf);
+}
+
+static int
+lkpi_set_chanctx_conf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_chanctx_conf *chanctx_conf,
+ uint32_t changed, bool changed_set)
+{
+ struct lkpi_hw *lhw;
+ struct lkpi_chanctx *lchanctx;
+ int error;
+
+ if (vif->bss_conf.chanctx_conf == chanctx_conf) {
+ if (!changed_set) {
+ IMPROVE("OBSOLETE?");
+ changed = IEEE80211_CHANCTX_CHANGE_MIN_WIDTH;
+ changed |= IEEE80211_CHANCTX_CHANGE_RADAR;
+ changed |= IEEE80211_CHANCTX_CHANGE_RX_CHAINS;
+ changed |= IEEE80211_CHANCTX_CHANGE_WIDTH;
+ }
+ lkpi_80211_mo_change_chanctx(hw, chanctx_conf, changed);
+
+ return (0);
+ }
+
+ lhw = HW_TO_LHW(hw);
+
+ /* The device is no longer idle. */
+ IMPROVE("Once we do multi-vif, only do for 1st chanctx");
+ lkpi_hw_conf_idle(hw, false);
+
+ error = lkpi_80211_mo_add_chanctx(hw, chanctx_conf);
+ if (error != 0 && error != EOPNOTSUPP) {
+ ic_printf(lhw->ic, "%s:%d: mo_add_chanctx "
+ "failed: %d\n", __func__, __LINE__, error);
+ return (error);
+ }
+
+ vif->bss_conf.chanreq.oper.chan = chanctx_conf->def.chan;
+ vif->bss_conf.chanreq.oper.width = chanctx_conf->def.width;
+ vif->bss_conf.chanreq.oper.center_freq1 =
+ chanctx_conf->def.center_freq1;
+ vif->bss_conf.chanreq.oper.center_freq2 =
+ chanctx_conf->def.center_freq2;
+
+ lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf);
+ list_add_rcu(&lchanctx->entry, &lhw->lchanctx_list);
+ rcu_assign_pointer(vif->bss_conf.chanctx_conf, chanctx_conf);
+
+ /* Assign vif chanctx. */
+ if (error == 0)
+ error = lkpi_80211_mo_assign_vif_chanctx(hw, vif,
+ &vif->bss_conf, chanctx_conf);
+ if (error == EOPNOTSUPP)
+ error = 0;
+ if (error != 0) {
+ ic_printf(lhw->ic, "%s:%d: mo_assign_vif_chanctx "
+ "failed: %d\n", __func__, __LINE__, error);
+ lkpi_80211_mo_remove_chanctx(hw, chanctx_conf);
+ rcu_assign_pointer(vif->bss_conf.chanctx_conf, NULL);
+ lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf);
+ list_del(&lchanctx->entry);
+ list_add_rcu(&lchanctx->entry, &lhw->lchanctx_list_reserved);
+ }
+
+ return (error);
+}
static void
lkpi_remove_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
{
+ struct lkpi_hw *lhw;
struct ieee80211_chanctx_conf *chanctx_conf;
struct lkpi_chanctx *lchanctx;
+ lockdep_assert_wiphy(hw->wiphy);
+
chanctx_conf = rcu_dereference_protected(vif->bss_conf.chanctx_conf,
lockdep_is_held(&hw->wiphy->mtx));
@@ -2227,27 +2459,79 @@ lkpi_remove_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
rcu_assign_pointer(vif->bss_conf.chanctx_conf, NULL);
lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf);
list_del(&lchanctx->entry);
- free(lchanctx, M_LKPI80211);
+ lhw = HW_TO_LHW(hw);
+ list_add_rcu(&lchanctx->entry, &lhw->lchanctx_list_reserved);
}
+/* -------------------------------------------------------------------------- */
+
+/* Any other options belong here? Check more drivers. */
+#define BSS_CHANGED_VIF_CFG_BITS \
+ (BSS_CHANGED_SSID | BSS_CHANGED_IDLE | BSS_CHANGED_PS | BSS_CHANGED_ASSOC | \
+ BSS_CHANGED_ARP_FILTER | BSS_CHANGED_MLD_VALID_LINKS | BSS_CHANGED_MLD_TTLM)
+
+static void
+lkpi_bss_info_change(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ enum ieee80211_bss_changed bss_changed)
+{
+ struct lkpi_vif *lvif;
+ enum ieee80211_bss_changed vif_cfg_bits, link_info_bits;
+
+ if (ieee80211_vif_is_mld(vif)) {
+ TODO("This likely needs a subset only; split up into 3 parts.");
+ }
+
+ /* Nothing to do? */
+ if (bss_changed == 0)
+ return;
+
+ /*
+ * If the vif is not known to the driver there is nothing to notifiy for.
+ * We MUST NOT check for !lvif_bss_synched here (the reasonable it seems)
+ * as we need to execute the update(s) or we will have follow-up issues.
+ */
+ lvif = VIF_TO_LVIF(vif);
+ if (!lvif->added_to_drv)
+ return;
+
+ /*
+ * With the advent of MLO bss_conf got split up into vif and link
+ * change notfications, while historically it was one.
+ * We now need to support all possible models.
+ */
+ vif_cfg_bits = bss_changed & BSS_CHANGED_VIF_CFG_BITS;
+ if (vif_cfg_bits != 0)
+ lkpi_80211_mo_vif_cfg_changed(hw, vif, vif_cfg_bits, false);
+
+ link_info_bits = bss_changed & ~(BSS_CHANGED_VIF_CFG_BITS);
+ if (link_info_bits != 0)
+ lkpi_80211_mo_link_info_changed(hw, vif, &vif->bss_conf,
+ link_info_bits, 0, false);
+
+ lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
+}
/* -------------------------------------------------------------------------- */
static int
lkpi_sta_state_do_nada(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
-
return (0);
}
-/* lkpi_iv_newstate() handles the stop scan case generally. */
-#define lkpi_sta_scan_to_init(_v, _n, _a) lkpi_sta_state_do_nada(_v, _n, _a)
+/* UP1 */
+static int
+lkpi_sta_init_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ return (lkpi_sta_state_do_nada(vap, nstate, arg));
+}
+/* UP2 */
static int
lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
struct linuxkpi_ieee80211_channel *chan;
- struct lkpi_chanctx *lchanctx;
+ struct cfg80211_chan_def chandef;
struct ieee80211_chanctx_conf *chanctx_conf;
struct lkpi_hw *lhw;
struct ieee80211_hw *hw;
@@ -2259,7 +2543,7 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int
struct ieee80211_prep_tx_info prep_tx_info;
uint32_t changed;
int error;
- bool synched;
+ bool synched, can_ht;
/*
* In here we use vap->iv_bss until lvif->lvif_bss is set.
@@ -2316,65 +2600,35 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int
wiphy_lock(hw->wiphy);
/* Add chanctx (or if exists, change it). */
- chanctx_conf = rcu_dereference_protected(vif->bss_conf.chanctx_conf,
- lockdep_is_held(&hw->wiphy->mtx));
- if (chanctx_conf != NULL) {
- lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf);
- IMPROVE("diff changes for changed, working on live copy, rcu");
- } else {
- /* Keep separate alloc as in Linux this is rcu managed? */
- lchanctx = malloc(sizeof(*lchanctx) + hw->chanctx_data_size,
- M_LKPI80211, M_WAITOK | M_ZERO);
- chanctx_conf = &lchanctx->chanctx_conf;
- }
+ chanctx_conf = lkpi_get_chanctx_conf(hw, vif);
- chanctx_conf->rx_chains_static = 1;
- chanctx_conf->rx_chains_dynamic = 1;
- chanctx_conf->radar_enabled =
- (chan->flags & IEEE80211_CHAN_RADAR) ? true : false;
- chanctx_conf->def.chan = chan;
- chanctx_conf->def.width = NL80211_CHAN_WIDTH_20_NOHT;
- chanctx_conf->def.center_freq1 = ieee80211_get_channel_center_freq1(ni->ni_chan);
- chanctx_conf->def.center_freq2 = ieee80211_get_channel_center_freq2(ni->ni_chan);
- IMPROVE("Check vht_cap from band not just chan?");
KASSERT(ni->ni_chan != NULL && ni->ni_chan != IEEE80211_CHAN_ANYC,
("%s:%d: ni %p ni_chan %p\n", __func__, __LINE__, ni, ni->ni_chan));
#ifdef LKPI_80211_HT
- if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) {
- if (IEEE80211_IS_CHAN_HT40(ni->ni_chan))
- chanctx_conf->def.width = NL80211_CHAN_WIDTH_40;
- else
- chanctx_conf->def.width = NL80211_CHAN_WIDTH_20;
- }
+ can_ht = (vap->iv_ic->ic_flags_ht & IEEE80211_FHT_HT) != 0;
+#else
+ can_ht = false;
#endif
-#ifdef LKPI_80211_VHT
- if (IEEE80211_IS_CHAN_VHT_5GHZ(ni->ni_chan)) {
- if (IEEE80211_IS_CHAN_VHT80P80(ni->ni_chan))
- chanctx_conf->def.width = NL80211_CHAN_WIDTH_80P80;
- else if (IEEE80211_IS_CHAN_VHT160(ni->ni_chan))
- chanctx_conf->def.width = NL80211_CHAN_WIDTH_160;
- else if (IEEE80211_IS_CHAN_VHT80(ni->ni_chan))
- chanctx_conf->def.width = NL80211_CHAN_WIDTH_80;
- }
+ lkpi_init_chandef(vap->iv_ic, &chandef, chan, ni->ni_chan, can_ht);
+ hw->conf.radar_enabled =
+ ((chan->flags & IEEE80211_CHAN_RADAR) != 0) ? true : false;
+ hw->conf.chandef = chandef;
+ vif->bss_conf.chanreq.oper = hw->conf.chandef;
+#ifdef LINUXKPI_DEBUG_80211
+ if ((linuxkpi_debug_80211 & D80211_CHANDEF) != 0)
+ ic_printf(vap->iv_ic, "%s:%d: hw->conf.chandef %p = chandef %p = "
+ "vif->bss_conf.chanreq.oper %p\n", __func__, __LINE__,
+ &hw->conf.chandef, &chandef, &vif->bss_conf.chanreq.oper);
#endif
- chanctx_conf->rx_chains_dynamic = lkpi_get_max_rx_chains(ni);
+
+ changed = lkpi_init_chanctx_conf(hw, &chandef, chanctx_conf);
+
/* Responder ... */
-#if 0
- chanctx_conf->min_def.chan = chanctx_conf->def.chan;
- chanctx_conf->min_def.width = NL80211_CHAN_WIDTH_20_NOHT;
-#ifdef LKPI_80211_HT
- if (IEEE80211_IS_CHAN_HT(ni->ni_chan) || IEEE80211_IS_CHAN_VHT(ni->ni_chan))
- chanctx_conf->min_def.width = NL80211_CHAN_WIDTH_20;
-#endif
- chanctx_conf->min_def.center_freq1 = chanctx_conf->def.center_freq1;
- chanctx_conf->min_def.center_freq2 = chanctx_conf->def.center_freq2;
-#else
- chanctx_conf->min_def = chanctx_conf->def;
-#endif
/* Set bss info (bss_info_changed). */
bss_changed = 0;
+ IEEE80211_ADDR_COPY(vif->cfg.ap_addr, ni->ni_bssid);
vif->bss_conf.bssid = ni->ni_bssid;
bss_changed |= BSS_CHANGED_BSSID;
vif->bss_conf.txpower = ni->ni_txpower;
@@ -2391,57 +2645,15 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int
bss_changed |= lkpi_update_dtim_tsf(vif, ni, vap, __func__, __LINE__);
- error = 0;
- if (vif->bss_conf.chanctx_conf == chanctx_conf) {
- changed = IEEE80211_CHANCTX_CHANGE_MIN_WIDTH;
- changed |= IEEE80211_CHANCTX_CHANGE_RADAR;
- changed |= IEEE80211_CHANCTX_CHANGE_RX_CHAINS;
- changed |= IEEE80211_CHANCTX_CHANGE_WIDTH;
- lkpi_80211_mo_change_chanctx(hw, chanctx_conf, changed);
- } else {
- /* The device is no longer idle. */
- IMPROVE("Once we do multi-vif, only do for 1st chanctx");
- lkpi_hw_conf_idle(hw, false);
-
- error = lkpi_80211_mo_add_chanctx(hw, chanctx_conf);
- if (error == 0 || error == EOPNOTSUPP) {
- vif->bss_conf.chanreq.oper.chan = chanctx_conf->def.chan;
- vif->bss_conf.chanreq.oper.width = chanctx_conf->def.width;
- vif->bss_conf.chanreq.oper.center_freq1 =
- chanctx_conf->def.center_freq1;
- vif->bss_conf.chanreq.oper.center_freq2 =
- chanctx_conf->def.center_freq2;
- } else {
- ic_printf(vap->iv_ic, "%s:%d: mo_add_chanctx "
- "failed: %d\n", __func__, __LINE__, error);
- goto out;
- }
-
- list_add_rcu(&lchanctx->entry, &lhw->lchanctx_list);
- rcu_assign_pointer(vif->bss_conf.chanctx_conf, chanctx_conf);
+ error = lkpi_set_chanctx_conf(hw, vif, chanctx_conf, changed, true);
+ if (error != 0)
+ goto out;
- /* Assign vif chanctx. */
- if (error == 0)
- error = lkpi_80211_mo_assign_vif_chanctx(hw, vif,
- &vif->bss_conf, chanctx_conf);
- if (error == EOPNOTSUPP)
- error = 0;
- if (error != 0) {
- ic_printf(vap->iv_ic, "%s:%d: mo_assign_vif_chanctx "
- "failed: %d\n", __func__, __LINE__, error);
- lkpi_80211_mo_remove_chanctx(hw, chanctx_conf);
- rcu_assign_pointer(vif->bss_conf.chanctx_conf, NULL);
- lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf);
- list_del(&lchanctx->entry);
- free(lchanctx, M_LKPI80211);
- goto out;
- }
- }
IMPROVE("update radiotap chan fields too");
/* RATES */
IMPROVE("bss info: not all needs to come now and rates are missing");
- lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
+ lkpi_bss_info_change(hw, vif, bss_changed);
/*
* Given ni and lsta are 1:1 from alloc to free we can assert that
@@ -2486,7 +2698,9 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int
/* Start mgd_prepare_tx. */
memset(&prep_tx_info, 0, sizeof(prep_tx_info));
- prep_tx_info.duration = PREP_TX_INFO_DURATION;
+ prep_tx_info.duration = PREP_TX_INFO_DURATION; /* SAE */
+ prep_tx_info.subtype = IEEE80211_STYPE_AUTH;
+ prep_tx_info.link_id = 0;
lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info);
lsta->in_mgd = true;
@@ -2567,136 +2781,7 @@ out_relocked:
return (error);
}
-static int
-lkpi_sta_auth_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
-{
- struct lkpi_hw *lhw;
- struct ieee80211_hw *hw;
- struct lkpi_vif *lvif;
- struct ieee80211_vif *vif;
- struct ieee80211_node *ni;
- struct lkpi_sta *lsta;
- struct ieee80211_sta *sta;
- struct ieee80211_prep_tx_info prep_tx_info;
- enum ieee80211_bss_changed bss_changed;
- int error;
-
- lhw = vap->iv_ic->ic_softc;
- hw = LHW_TO_HW(lhw);
- lvif = VAP_TO_LVIF(vap);
- vif = LVIF_TO_VIF(lvif);
-
- LKPI_80211_LVIF_LOCK(lvif);
-#ifdef LINUXKPI_DEBUG_80211
- /* XXX-BZ KASSERT later; state going down so no action. */
- if (lvif->lvif_bss == NULL)
- ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p "
- "lvif_bss->ni %p synched %d\n", __func__, __LINE__,
- lvif, vap, vap->iv_bss, lvif->lvif_bss,
- (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
- lvif->lvif_bss_synched);
-#endif
-
- lsta = lvif->lvif_bss;
- LKPI_80211_LVIF_UNLOCK(lvif);
- KASSERT(lsta != NULL && lsta->ni != NULL, ("%s: lsta %p ni %p "
- "lvif %p vap %p\n", __func__,
- lsta, (lsta != NULL) ? lsta->ni : NULL, lvif, vap));
- ni = lsta->ni; /* Reference held for lvif_bss. */
- sta = LSTA_TO_STA(lsta);
-
- lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
-
- IEEE80211_UNLOCK(vap->iv_ic);
- wiphy_lock(hw->wiphy);
-
- /* flush, drop. */
- lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), true);
-
- /* Wake tx queues to get packet(s) out. */
- lkpi_wake_tx_queues(hw, sta, false, true);
-
- /* flush, no drop */
- lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), false);
-
- /* End mgd_complete_tx. */
- if (lsta->in_mgd) {
- memset(&prep_tx_info, 0, sizeof(prep_tx_info));
- prep_tx_info.success = false;
- lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info);
- lsta->in_mgd = false;
- }
-
- /* sync_rx_queues */
- lkpi_80211_mo_sync_rx_queues(hw);
-
- /* sta_pre_rcu_remove */
- lkpi_80211_mo_sta_pre_rcu_remove(hw, vif, sta);
-
- /* Take the station down. */
-
- /* Adjust sta and change state (from NONE) to NOTEXIST. */
- KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
- KASSERT(lsta->state == IEEE80211_STA_NONE, ("%s: lsta %p state not "
- "NONE: %#x, nstate %d arg %d\n", __func__, lsta, lsta->state, nstate, arg));
- error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_NOTEXIST);
- if (error != 0) {
- IMPROVE("do we need to undo the chan ctx?");
- ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(NOTEXIST) "
- "failed: %d\n", __func__, __LINE__, error);
- goto out;
- }
-#if 0
- lsta->added_to_drv = false; /* mo manages. */
-#endif
-
- bss_changed = 0;
- vif->bss_conf.dtim_period = 0; /* go back to 0. */
- bss_changed |= BSS_CHANGED_BEACON_INFO;
- lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
-
- lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
-
- LKPI_80211_LVIF_LOCK(lvif);
- /* Remove ni reference for this cache of lsta. */
- lvif->lvif_bss = NULL;
- lvif->lvif_bss_synched = false;
- LKPI_80211_LVIF_UNLOCK(lvif);
- lkpi_lsta_remove(lsta, lvif);
-
- /* conf_tx */
-
- lkpi_remove_chanctx(hw, vif);
-
-out:
- wiphy_unlock(hw->wiphy);
- IEEE80211_LOCK(vap->iv_ic);
- if (error == 0) {
- /*
- * We do this outside the wiphy lock as net80211::node_free() may call
- * into crypto code to delete keys and we have a recursed on
- * non-recursive sx panic. Also only do this if we get here w/o error.
- *
- * The very last release the reference on the ni for the ni/lsta on
- * lvif->lvif_bss. Upon return from this both ni and lsta are invalid
- * and potentially freed.
- */
- ieee80211_free_node(ni);
- }
- return (error);
-}
-
-static int
-lkpi_sta_auth_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
-{
- int error;
-
- error = lkpi_sta_auth_to_scan(vap, nstate, arg);
- if (error == 0)
- error = lkpi_sta_scan_to_init(vap, nstate, arg);
- return (error);
-}
-
+/* UP3.1 */
static int
lkpi_sta_auth_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
@@ -2751,23 +2836,28 @@ lkpi_sta_auth_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, in
/* End mgd_complete_tx. */
if (lsta->in_mgd) {
memset(&prep_tx_info, 0, sizeof(prep_tx_info));
+ prep_tx_info.subtype = IEEE80211_STYPE_AUTH;
prep_tx_info.success = true;
lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info);
lsta->in_mgd = false;
}
- /* Now start assoc. */
+ /* Now start assoc. unless nstate=RUN (auth_to_run). */
/* Start mgd_prepare_tx. */
- if (!lsta->in_mgd) {
+ if (nstate == IEEE80211_S_ASSOC && !lsta->in_mgd) {
memset(&prep_tx_info, 0, sizeof(prep_tx_info));
- prep_tx_info.duration = PREP_TX_INFO_DURATION;
+ prep_tx_info.subtype = IEEE80211_STYPE_ASSOC_REQ;
+ prep_tx_info.link_id = 0;
lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info);
lsta->in_mgd = true;
}
+#if 0
+ /* We do not yet have a packet to go out. */
/* Wake tx queue to get packet out. */
lkpi_wake_tx_queues(hw, LSTA_TO_STA(lsta), false, true);
+#endif
/*
* <twiddle> .. we end up in "assoc_to_run"
@@ -2785,286 +2875,21 @@ out:
return (error);
}
-/* auth_to_auth, assoc_to_assoc. */
-static int
-lkpi_sta_a_to_a(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
-{
- struct lkpi_hw *lhw;
- struct ieee80211_hw *hw;
- struct lkpi_vif *lvif;
- struct ieee80211_vif *vif;
- struct lkpi_sta *lsta;
- struct ieee80211_prep_tx_info prep_tx_info;
- int error;
-
- lhw = vap->iv_ic->ic_softc;
- hw = LHW_TO_HW(lhw);
- lvif = VAP_TO_LVIF(vap);
- vif = LVIF_TO_VIF(lvif);
-
- IEEE80211_UNLOCK(vap->iv_ic);
- wiphy_lock(hw->wiphy);
-
- LKPI_80211_LVIF_LOCK(lvif);
- /* XXX-BZ KASSERT later? */
- if (!lvif->lvif_bss_synched || lvif->lvif_bss == NULL) {
-#ifdef LINUXKPI_DEBUG_80211
- ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p "
- "lvif_bss->ni %p synched %d\n", __func__, __LINE__,
- lvif, vap, vap->iv_bss, lvif->lvif_bss,
- (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
- lvif->lvif_bss_synched);
-#endif
- LKPI_80211_LVIF_UNLOCK(lvif);
- error = ENOTRECOVERABLE;
- goto out;
- }
- lsta = lvif->lvif_bss;
- LKPI_80211_LVIF_UNLOCK(lvif);
-
- KASSERT(lsta != NULL, ("%s: lsta %p! lvif %p vap %p\n", __func__,
- lsta, lvif, vap));
-
- IMPROVE("event callback?");
-
- /* End mgd_complete_tx. */
- if (lsta->in_mgd) {
- memset(&prep_tx_info, 0, sizeof(prep_tx_info));
- prep_tx_info.success = false;
- lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info);
- lsta->in_mgd = false;
- }
-
- /* Now start assoc. */
-
- /* Start mgd_prepare_tx. */
- if (!lsta->in_mgd) {
- memset(&prep_tx_info, 0, sizeof(prep_tx_info));
- prep_tx_info.duration = PREP_TX_INFO_DURATION;
- lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info);
- lsta->in_mgd = true;
- }
-
- error = 0;
-out:
- wiphy_unlock(hw->wiphy);
- IEEE80211_LOCK(vap->iv_ic);
-
- return (error);
-}
-
-static int
-_lkpi_sta_assoc_to_down(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
-{
- struct lkpi_hw *lhw;
- struct ieee80211_hw *hw;
- struct lkpi_vif *lvif;
- struct ieee80211_vif *vif;
- struct ieee80211_node *ni;
- struct lkpi_sta *lsta;
- struct ieee80211_sta *sta;
- struct ieee80211_prep_tx_info prep_tx_info;
- enum ieee80211_bss_changed bss_changed;
- int error;
-
- lhw = vap->iv_ic->ic_softc;
- hw = LHW_TO_HW(lhw);
- lvif = VAP_TO_LVIF(vap);
- vif = LVIF_TO_VIF(lvif);
-
- IEEE80211_UNLOCK(vap->iv_ic);
- wiphy_lock(hw->wiphy);
-
- LKPI_80211_LVIF_LOCK(lvif);
-#ifdef LINUXKPI_DEBUG_80211
- /* XXX-BZ KASSERT later; state going down so no action. */
- if (lvif->lvif_bss == NULL)
- ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p "
- "lvif_bss->ni %p synched %d\n", __func__, __LINE__,
- lvif, vap, vap->iv_bss, lvif->lvif_bss,
- (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
- lvif->lvif_bss_synched);
-#endif
- lsta = lvif->lvif_bss;
- LKPI_80211_LVIF_UNLOCK(lvif);
- KASSERT(lsta != NULL && lsta->ni != NULL, ("%s: lsta %p ni %p "
- "lvif %p vap %p\n", __func__,
- lsta, (lsta != NULL) ? lsta->ni : NULL, lvif, vap));
-
- ni = lsta->ni; /* Reference held for lvif_bss. */
- sta = LSTA_TO_STA(lsta);
-
- lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
-
- /* flush, drop. */
- lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), true);
-
- IMPROVE("What are the proper conditions for DEAUTH_NEED_MGD_TX_PREP?");
- if (ieee80211_hw_check(hw, DEAUTH_NEED_MGD_TX_PREP) &&
- !lsta->in_mgd) {
- memset(&prep_tx_info, 0, sizeof(prep_tx_info));
- prep_tx_info.duration = PREP_TX_INFO_DURATION;
- prep_tx_info.was_assoc = true;
- lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info);
- lsta->in_mgd = true;
- }
-
- wiphy_unlock(hw->wiphy);
- IEEE80211_LOCK(vap->iv_ic);
-
- /* Call iv_newstate first so we get potential DEAUTH packet out. */
- error = lvif->iv_newstate(vap, nstate, arg);
- if (error != 0) {
- ic_printf(vap->iv_ic, "%s:%d: iv_newstate(%p, %d, %d) "
- "failed: %d\n", __func__, __LINE__, vap, nstate, arg, error);
- goto outni;
- }
-
- IEEE80211_UNLOCK(vap->iv_ic);
-
- /* Ensure the packets get out. */
- lkpi_80211_flush_tx(lhw, lsta);
-
- wiphy_lock(hw->wiphy);
-
- lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
-
- /* Wake tx queues to get packet(s) out. */
- lkpi_wake_tx_queues(hw, sta, false, true);
-
- /* flush, no drop */
- lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), false);
-
- /* End mgd_complete_tx. */
- if (lsta->in_mgd) {
- memset(&prep_tx_info, 0, sizeof(prep_tx_info));
- prep_tx_info.success = false;
- prep_tx_info.was_assoc = true;
- lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info);
- lsta->in_mgd = false;
- }
-
- /* sync_rx_queues */
- lkpi_80211_mo_sync_rx_queues(hw);
-
- /* sta_pre_rcu_remove */
- lkpi_80211_mo_sta_pre_rcu_remove(hw, vif, sta);
-
- /* Take the station down. */
-
- /* Update sta and change state (from AUTH) to NONE. */
- KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
- KASSERT(lsta->state == IEEE80211_STA_AUTH, ("%s: lsta %p state not "
- "AUTH: %#x\n", __func__, lsta, lsta->state));
- error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_NONE);
- if (error != 0) {
- ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(NONE) "
- "failed: %d\n", __func__, __LINE__, error);
- goto out;
- }
-
- /* See comment in lkpi_sta_run_to_init(). */
- bss_changed = 0;
- bss_changed |= lkpi_disassoc(sta, vif, lhw);
-
-#ifdef LKPI_80211_HW_CRYPTO
- /*
- * In theory we remove keys here but there must not exist any for this
- * state change until we clean them up again into small steps and no
- * code duplication.
- */
-#endif
-
- lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
-
- /* Adjust sta and change state (from NONE) to NOTEXIST. */
- KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
- KASSERT(lsta->state == IEEE80211_STA_NONE, ("%s: lsta %p state not "
- "NONE: %#x, nstate %d arg %d\n", __func__, lsta, lsta->state, nstate, arg));
- error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_NOTEXIST);
- if (error != 0) {
- IMPROVE("do we need to undo the chan ctx?");
- ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(NOTEXIST) "
- "failed: %d\n", __func__, __LINE__, error);
- goto out;
- }
-
- lkpi_lsta_dump(lsta, ni, __func__, __LINE__); /* sta no longer save to use. */
-
- IMPROVE("Any bss_info changes to announce?");
- vif->bss_conf.qos = 0;
- bss_changed |= BSS_CHANGED_QOS;
- vif->cfg.ssid_len = 0;
- memset(vif->cfg.ssid, '\0', sizeof(vif->cfg.ssid));
- bss_changed |= BSS_CHANGED_BSSID;
- vif->bss_conf.dtim_period = 0; /* go back to 0. */
- bss_changed |= BSS_CHANGED_BEACON_INFO;
- lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
-
- LKPI_80211_LVIF_LOCK(lvif);
- /* Remove ni reference for this cache of lsta. */
- lvif->lvif_bss = NULL;
- lvif->lvif_bss_synched = false;
- LKPI_80211_LVIF_UNLOCK(lvif);
- lkpi_lsta_remove(lsta, lvif);
-
- /* conf_tx */
-
- lkpi_remove_chanctx(hw, vif);
-
- error = EALREADY;
-out:
- wiphy_unlock(hw->wiphy);
- IEEE80211_LOCK(vap->iv_ic);
- if (error == EALREADY) {
- /*
- * We do this outside the wiphy lock as net80211::node_free() may call
- * into crypto code to delete keys and we have a recursed on
- * non-recursive sx panic. Also only do this if we get here w/o error.
- *
- * The very last release the reference on the ni for the ni/lsta on
- * lvif->lvif_bss. Upon return from this both ni and lsta are invalid
- * and potentially freed.
- */
- ieee80211_free_node(ni);
- }
-outni:
- return (error);
-}
-
-static int
-lkpi_sta_assoc_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
-{
- int error;
-
- error = _lkpi_sta_assoc_to_down(vap, nstate, arg);
- if (error != 0 && error != EALREADY)
- return (error);
-
- /* At this point iv_bss is long a new node! */
-
- error |= lkpi_sta_scan_to_auth(vap, nstate, 0);
- return (error);
-}
-
-static int
-lkpi_sta_assoc_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
-{
- int error;
-
- error = _lkpi_sta_assoc_to_down(vap, nstate, arg);
- return (error);
-}
+static int lkpi_sta_assoc_to_run(struct ieee80211vap *, enum ieee80211_state, int);
+/* UP3.2 */
static int
-lkpi_sta_assoc_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+lkpi_sta_auth_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
int error;
- error = _lkpi_sta_assoc_to_down(vap, nstate, arg);
+ error = lkpi_sta_auth_to_assoc(vap, nstate, arg);
+ if (error == 0)
+ error = lkpi_sta_assoc_to_run(vap, nstate, arg);
return (error);
}
+/* UP4 */
static int
lkpi_sta_assoc_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
@@ -3112,7 +2937,7 @@ lkpi_sta_assoc_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int
IMPROVE("ponder some of this moved to ic_newassoc, scan_assoc_success, "
"and to lesser extend ieee80211_notify_node_join");
- /* Finish assoc. */
+ /* Finish assoc. (even if this is auth_to_run!) */
/* Update sta_state (AUTH to ASSOC) and set aid. */
KASSERT(lsta->state == IEEE80211_STA_AUTH, ("%s: lsta %p state not "
"AUTH: %#x\n", __func__, lsta, lsta->state));
@@ -3162,16 +2987,18 @@ lkpi_sta_assoc_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int
}
bss_changed |= lkpi_update_dtim_tsf(vif, ni, vap, __func__, __LINE__);
- lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
+ lkpi_bss_info_change(hw, vif, bss_changed);
/* - change_chanctx (if needed)
* - event_callback
*/
- /* End mgd_complete_tx. */
+ /* End mgd_complete_tx. (we do not have to check ostate == IEEE80211_S_ASSOC). */
if (lsta->in_mgd) {
memset(&prep_tx_info, 0, sizeof(prep_tx_info));
- prep_tx_info.success = true;
+ prep_tx_info.subtype = IEEE80211_STYPE_ASSOC_REQ;
+ prep_tx_info.success = true; /* Needs vif->cfg.assoc set! */
+ prep_tx_info.link_id = 0;
lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info);
lsta->in_mgd = false;
}
@@ -3220,7 +3047,7 @@ lkpi_sta_assoc_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int
bss_changed = 0;
bss_changed |= lkpi_update_dtim_tsf(vif, ni, vap, __func__, __LINE__);
- lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
+ lkpi_bss_info_change(hw, vif, bss_changed);
/* Prepare_multicast && configure_filter. */
lkpi_update_mcast_filter(vap->iv_ic);
@@ -3231,17 +3058,14 @@ out:
return (error);
}
-static int
-lkpi_sta_auth_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
-{
- int error;
-
- error = lkpi_sta_auth_to_assoc(vap, nstate, arg);
- if (error == 0)
- error = lkpi_sta_assoc_to_run(vap, nstate, arg);
- return (error);
-}
-
+/*
+ * DOWN1
+ * "to assoc" means we are going back to State 2 from State 4[/3].
+ * This means ni still is authenticated, so we keep sta, chanctx, ..
+ * We will send a (Re)Assoc Request in case net80211 handles roadming.
+ * Note: this can be called as part of a DEAUTH going to State 1 as well,
+ * so for RoC prep_tx_info we need to check nstate (see run_to_{auth,scan,init}).
+ */
static int
lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
@@ -3256,6 +3080,7 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int
#if 0
enum ieee80211_bss_changed bss_changed;
#endif
+ struct ieee80211_rx_ampdu *rap;
int error;
lhw = vap->iv_ic->ic_softc;
@@ -3263,6 +3088,9 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int
lvif = VAP_TO_LVIF(vap);
vif = LVIF_TO_VIF(lvif);
+ IEEE80211_UNLOCK(vap->iv_ic);
+ wiphy_lock(hw->wiphy);
+
LKPI_80211_LVIF_LOCK(lvif);
#ifdef LINUXKPI_DEBUG_80211
/* XXX-BZ KASSERT later; state going down so no action. */
@@ -3284,26 +3112,41 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int
lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
- IEEE80211_UNLOCK(vap->iv_ic);
- wiphy_lock(hw->wiphy);
-
/* flush, drop. */
lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), true);
- IMPROVE("What are the proper conditions for DEAUTH_NEED_MGD_TX_PREP?");
- if (ieee80211_hw_check(hw, DEAUTH_NEED_MGD_TX_PREP) &&
- !lsta->in_mgd) {
- memset(&prep_tx_info, 0, sizeof(prep_tx_info));
- prep_tx_info.duration = PREP_TX_INFO_DURATION;
- prep_tx_info.was_assoc = true;
- lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info);
- lsta->in_mgd = true;
+ /* We should make this a KASSERT. */
+ if (lsta->in_mgd) {
+ ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p lsta %p in_mgd\n",
+ __func__, __LINE__, lvif, vap, lsta);
}
+ /*
+ * Problem is that we should hook into the tx/rx flow and not
+ * try to re-model the state machine parts. We may miss a SME
+ * triggered frame this way.
+ */
+ memset(&prep_tx_info, 0, sizeof(prep_tx_info));
+ if (nstate == IEEE80211_S_ASSOC) {
+ if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) {
+ if (arg)
+ prep_tx_info.subtype = IEEE80211_STYPE_REASSOC_REQ;
+ else
+ prep_tx_info.subtype = IEEE80211_STYPE_ASSOC_REQ;
+ } else {
+ /* wpa_supplicant upon RTM_IEEE80211_LEAVE. */
+ prep_tx_info.subtype = IEEE80211_STYPE_DISASSOC;
+ }
+ } else
+ prep_tx_info.subtype = IEEE80211_STYPE_DEAUTH;
+ prep_tx_info.was_assoc = true;
+ prep_tx_info.link_id = 0;
+ lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info);
+ lsta->in_mgd = true;
wiphy_unlock(hw->wiphy);
IEEE80211_LOCK(vap->iv_ic);
- /* Call iv_newstate first so we get potential DISASSOC packet out. */
+ /* Call iv_newstate first so we get potential (RE-)ASSOC/DEAUTH? packet out. */
error = lvif->iv_newstate(vap, nstate, arg);
if (error != 0) {
ic_printf(vap->iv_ic, "%s:%d: iv_newstate(%p, %d, %d) "
@@ -3311,6 +3154,16 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int
goto outni;
}
+ /* Stop any BA sessions if still active. */
+ for (int rapn = 0; rapn < WME_NUM_TID; rapn++) {
+ rap = &ni->ni_rx_ampdu[rapn];
+
+ if ((rap->rxa_flags & IEEE80211_AGGR_RUNNING) == 0)
+ continue;
+
+ vap->iv_ic->ic_ampdu_rx_stop(ni, rap);
+ }
+
IEEE80211_UNLOCK(vap->iv_ic);
/* Ensure the packets get out. */
@@ -3327,20 +3180,20 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int
lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), false);
/* End mgd_complete_tx. */
- if (lsta->in_mgd) {
- memset(&prep_tx_info, 0, sizeof(prep_tx_info));
- prep_tx_info.success = false;
- prep_tx_info.was_assoc = true;
- lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info);
- lsta->in_mgd = false;
+ /* We should make this a KASSERT. */
+ if (!lsta->in_mgd) {
+ ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p lsta %p !in_mgd\n",
+ __func__, __LINE__, lvif, vap, lsta);
}
+ lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info);
+ lsta->in_mgd = false;
#if 0
/* sync_rx_queues */
lkpi_80211_mo_sync_rx_queues(hw);
/* sta_pre_rcu_remove */
- lkpi_80211_mo_sta_pre_rcu_remove(hw, vif, sta);
+ lkpi_80211_mo_sta_pre_rcu_remove(hw, vif, sta);
#endif
/* Take the station down. */
@@ -3389,6 +3242,7 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int
#if 0
/* Update bss info (bss_info_changed) (assoc, aid, ..). */
+ /* See comment in DOWN4. */
lkpi_disassoc(sta, vif, lhw);
#endif
@@ -3400,8 +3254,15 @@ outni:
return (error);
}
+/*
+ * DOWN2
+ * We are in state 2 and go back to state 1 and will try to auth again
+ * (to IEEE80211_S_AUTH in FreeBSD means "try to auth"). This should be
+ * like scan_to_auth but that we keep the "ni" and with that chanctx/bssid,
+ * which essentially makes this "a_to_a" in LinuxKPI.
+ */
static int
-lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+lkpi_sta_assoc_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
struct lkpi_hw *lhw;
struct ieee80211_hw *hw;
@@ -3409,9 +3270,7 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
struct ieee80211_vif *vif;
struct ieee80211_node *ni;
struct lkpi_sta *lsta;
- struct ieee80211_sta *sta;
struct ieee80211_prep_tx_info prep_tx_info;
- enum ieee80211_bss_changed bss_changed;
int error;
lhw = vap->iv_ic->ic_softc;
@@ -3439,43 +3298,107 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
lsta, (lsta != NULL) ? lsta->ni : NULL, lvif, vap));
ni = lsta->ni; /* Reference held for lvif_bss. */
- sta = LSTA_TO_STA(lsta);
lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
- /* flush, drop. */
- lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), true);
-
- IMPROVE("What are the proper conditions for DEAUTH_NEED_MGD_TX_PREP?");
- if (ieee80211_hw_check(hw, DEAUTH_NEED_MGD_TX_PREP) &&
- !lsta->in_mgd) {
+ /* End mgd_complete_tx. */
+ if (lsta->in_mgd && vap->iv_state == IEEE80211_S_ASSOC) {
memset(&prep_tx_info, 0, sizeof(prep_tx_info));
- prep_tx_info.duration = PREP_TX_INFO_DURATION;
- prep_tx_info.was_assoc = true;
- lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info);
- lsta->in_mgd = true;
+ prep_tx_info.subtype = IEEE80211_STYPE_ASSOC_REQ;
+ prep_tx_info.link_id = 0;
+ lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info);
+ lsta->in_mgd = false;
+ } else if (lsta->in_mgd) {
+ ic_printf(vap->iv_ic, "%s:%d: in_mgd %d (%s) -> %d (%s) %d\n",
+ __func__, __LINE__,
+ vap->iv_state, ieee80211_state_name[vap->iv_state],
+ nstate, ieee80211_state_name[nstate], arg);
}
- wiphy_unlock(hw->wiphy);
- IEEE80211_LOCK(vap->iv_ic);
-
- /* Call iv_newstate first so we get potential DISASSOC packet out. */
- error = lvif->iv_newstate(vap, nstate, arg);
+ /* Take the station down. */
+ /* Update sta_state (AUTH to NONE). */
+ KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
+ KASSERT(lsta->state == IEEE80211_STA_AUTH, ("%s: lsta %p state not "
+ "AUTH: %#x\n", __func__, lsta, lsta->state));
+ error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_NONE);
if (error != 0) {
- ic_printf(vap->iv_ic, "%s:%d: iv_newstate(%p, %d, %d) "
- "failed: %d\n", __func__, __LINE__, vap, nstate, arg, error);
- goto outni;
+ ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(NONE) "
+ "failed: %d\n", __func__, __LINE__, error);
+ goto out;
}
- IEEE80211_UNLOCK(vap->iv_ic);
+ lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
- /* Ensure the packets get out. */
- lkpi_80211_flush_tx(lhw, lsta);
+out:
+ wiphy_unlock(hw->wiphy);
+ IEEE80211_LOCK(vap->iv_ic);
+ return (error);
+}
+/*
+ * DOWN3
+ * We are in state 1. Either auth timed out (arg != 0) or we have an internal
+ * state change forcing us to give up trying to authenticate.
+ * Cleanup and remove chanctx, sta, ...
+ */
+static int
+lkpi_sta_auth_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ struct lkpi_hw *lhw;
+ struct ieee80211_hw *hw;
+ struct lkpi_vif *lvif;
+ struct ieee80211_vif *vif;
+ struct ieee80211_node *ni;
+ struct lkpi_sta *lsta;
+ struct ieee80211_sta *sta;
+ struct ieee80211_prep_tx_info prep_tx_info;
+ enum ieee80211_bss_changed bss_changed;
+ int error;
+
+ lhw = vap->iv_ic->ic_softc;
+ hw = LHW_TO_HW(lhw);
+ lvif = VAP_TO_LVIF(vap);
+ vif = LVIF_TO_VIF(lvif);
+
+ IEEE80211_UNLOCK(vap->iv_ic);
wiphy_lock(hw->wiphy);
+ LKPI_80211_LVIF_LOCK(lvif);
+ /*
+ * XXX-BZ KASSERT later; state going down so no action in theory
+ * but try to avoid a NULL-pointer derref for now and gracefully
+ * fail for non-debug kernels.
+ */
+ if (lvif->lvif_bss == NULL) {
+ ic_printf(vap->iv_ic, "%s:%d: ERROR: lvif %p vap %p iv_bss %p "
+ "lvif_bss %p lvif_bss->ni %p synched %d; "
+ "expect follow-up problems\n", __func__, __LINE__,
+ lvif, vap, vap->iv_bss, lvif->lvif_bss,
+ (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
+ lvif->lvif_bss_synched);
+ LKPI_80211_LVIF_UNLOCK(lvif);
+ /*
+ * This will likely lead to a firmware crash (if there
+ * was not one before already) and need a
+ * ieee80211_restart_hw() but still better than a panic
+ * for users as they can at least recover.
+ */
+ error = ENOTRECOVERABLE;
+ goto out;
+ }
+ lsta = lvif->lvif_bss;
+ LKPI_80211_LVIF_UNLOCK(lvif);
+ KASSERT(lsta != NULL && lsta->ni != NULL, ("%s: lsta %p ni %p "
+ "lvif %p vap %p\n", __func__,
+ lsta, (lsta != NULL) ? lsta->ni : NULL, lvif, vap));
+ ni = lsta->ni; /* Reference held for lvif_bss. */
+ sta = LSTA_TO_STA(lsta);
+
lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
+ /* flush, drop. */
+ lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), true);
+
/* Wake tx queues to get packet(s) out. */
lkpi_wake_tx_queues(hw, sta, false, true);
@@ -3485,8 +3408,8 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
/* End mgd_complete_tx. */
if (lsta->in_mgd) {
memset(&prep_tx_info, 0, sizeof(prep_tx_info));
- prep_tx_info.success = false;
- prep_tx_info.was_assoc = true;
+ prep_tx_info.subtype = IEEE80211_STYPE_AUTH;
+ prep_tx_info.link_id = 0;
lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info);
lsta->in_mgd = false;
}
@@ -3494,36 +3417,8 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
/* sync_rx_queues */
lkpi_80211_mo_sync_rx_queues(hw);
- /* sta_pre_rcu_remove */
- lkpi_80211_mo_sta_pre_rcu_remove(hw, vif, sta);
-
- /* Take the station down. */
-
- /* Adjust sta and change state (from AUTHORIZED) to ASSOC. */
- KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
- KASSERT(lsta->state == IEEE80211_STA_AUTHORIZED, ("%s: lsta %p state not "
- "AUTHORIZED: %#x\n", __func__, lsta, lsta->state));
- error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_ASSOC);
- if (error != 0) {
- ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(ASSOC) "
- "failed: %d\n", __func__, __LINE__, error);
- goto out;
- }
-
- lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
-
#ifdef LKPI_80211_HW_CRYPTO
if (lkpi_hwcrypto) {
- /*
- * In theory we only need to do this if we changed assoc.
- * If we were not assoc, there should be no keys and we
- * should not be here.
- */
-#ifdef notyet
- KASSERT((bss_changed & BSS_CHANGED_ASSOC) != 0, ("%s: "
- "trying to remove keys but were not assoc: %#010jx, lvif %p\n",
- __func__, (uintmax_t)bss_changed, lvif));
-#endif
error = lkpi_sta_del_keys(hw, vif, lsta);
if (error != 0) {
ic_printf(vap->iv_ic, "%s:%d: lkpi_sta_del_keys "
@@ -3538,50 +3433,36 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
}
#endif
- /* Update sta_state (ASSOC to AUTH). */
- KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
- KASSERT(lsta->state == IEEE80211_STA_ASSOC, ("%s: lsta %p state not "
- "ASSOC: %#x\n", __func__, lsta, lsta->state));
- error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_AUTH);
- if (error != 0) {
- ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(AUTH) "
- "failed: %d\n", __func__, __LINE__, error);
- goto out;
- }
+ /* sta_pre_rcu_remove */
+ lkpi_80211_mo_sta_pre_rcu_remove(hw, vif, sta);
- lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
+ synchronize_net();
- /* Update sta and change state (from AUTH) to NONE. */
- KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
- KASSERT(lsta->state == IEEE80211_STA_AUTH, ("%s: lsta %p state not "
- "AUTH: %#x\n", __func__, lsta, lsta->state));
- error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_NONE);
- if (error != 0) {
- ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(NONE) "
- "failed: %d\n", __func__, __LINE__, error);
- goto out;
- }
+ /* Take the station down. */
bss_changed = 0;
/*
- * Start updating bss info (bss_info_changed) (assoc, aid, ..).
+ * Start updating bss info (*bss_info_changed) (assoc, aid, ..).
*
- * One would expect this to happen when going off AUTHORIZED.
- * See comment there; removes the sta from fw if not careful
- * (bss_info_changed() change is executed right away).
+ * One would expect this to happen when going off AUTHORIZED but
+ * not so.
*
- * We need to do this now, before sta changes to IEEE80211_STA_NOTEXIST
- * as otherwise drivers (iwlwifi at least) will silently not remove
- * the sta from the firmware and when we will add a new one trigger
- * a fw assert.
+ * Immediately issuing the (*bss_info_changed) used to also remove the
+ * sta from firmware for iwlwifi; or we have problems with the sta
+ * silently not being removed and then crash upon the next sta add.
+ * Neither seems to be the case or a problem still.
*
- * The order which works best so far avoiding early removal or silent
- * non-removal seems to be (for iwlwifi::mld-mac80211.c cases;
- * the iwlwifi:mac80211.c case still to be tested):
- * 1) lkpi_disassoc(): set vif->cfg.assoc = false (aid=0 side effect here)
- * 2) call the last sta_state update -> IEEE80211_STA_NOTEXIST
- * (removes the sta given assoc is false)
- * 3) add the remaining BSS_CHANGED changes and call bss_info_changed()
+ * Contrary for BE200 (iwlwifi/mld) if we do not issue the
+ * (*vif_cfg_change) to tell FW that we are no longer assoc
+ * it will crash now upon sta rm. So the order now is as we once
+ * expected it:
+ *
+ * 1) lkpi_disassoc(): set vif->cfg.assoc = false and .aid=0
+ * 2) add the remaining BSS_CHANGED changes and call (*bss_info_changed)
+ * (which may be split up into (*vif_cfg_change) and
+ * (*link_info_changed) for more modern drivers).
+ * 3) call the last sta_state update -> IEEE80211_STA_NOTEXIST
+ * (removes the sta given assoc is false) and tidy up our lists.
* 4) call unassign_vif_chanctx
* 5) call lkpi_hw_conf_idle
* 6) call remove_chanctx
@@ -3593,6 +3474,19 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
+ IMPROVE("Any bss_info changes to announce?");
+ vif->bss_conf.qos = false;
+ bss_changed |= BSS_CHANGED_QOS;
+ vif->cfg.ssid_len = 0;
+ memset(vif->cfg.ssid, '\0', sizeof(vif->cfg.ssid));
+ IEEE80211_ADDR_COPY(vif->cfg.ap_addr, ieee80211broadcastaddr);
+ bss_changed |= BSS_CHANGED_BSSID;
+ vif->bss_conf.use_short_preamble = false;
+ /* XXX BSS_CHANGED_???? */
+ vif->bss_conf.dtim_period = 0; /* go back to 0. */
+ bss_changed |= BSS_CHANGED_BEACON_INFO;
+ lkpi_bss_info_change(hw, vif, bss_changed);
+
/* Adjust sta and change state (from NONE) to NOTEXIST. */
KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
KASSERT(lsta->state == IEEE80211_STA_NONE, ("%s: lsta %p state not "
@@ -3607,20 +3501,7 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
lkpi_lsta_remove(lsta, lvif);
- lkpi_lsta_dump(lsta, ni, __func__, __LINE__); /* sta no longer save to use. */
-
- IMPROVE("Any bss_info changes to announce?");
- vif->bss_conf.qos = 0;
- bss_changed |= BSS_CHANGED_QOS;
- vif->cfg.ssid_len = 0;
- memset(vif->cfg.ssid, '\0', sizeof(vif->cfg.ssid));
- bss_changed |= BSS_CHANGED_BSSID;
- vif->bss_conf.use_short_preamble = false;
- vif->bss_conf.qos = false;
- /* XXX BSS_CHANGED_???? */
- vif->bss_conf.dtim_period = 0; /* go back to 0. */
- bss_changed |= BSS_CHANGED_BEACON_INFO;
- lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
+ lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
LKPI_80211_LVIF_LOCK(lvif);
/* Remove ni reference for this cache of lsta. */
@@ -3632,11 +3513,10 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
lkpi_remove_chanctx(hw, vif);
- error = EALREADY;
out:
wiphy_unlock(hw->wiphy);
IEEE80211_LOCK(vap->iv_ic);
- if (error == EALREADY) {
+ if (error == 0) {
/*
* We do this outside the wiphy lock as net80211::node_free() may call
* into crypto code to delete keys and we have a recursed on
@@ -3648,15 +3528,156 @@ out:
*/
ieee80211_free_node(ni);
}
-outni:
+ return (error);
+}
+
+/* DOWN4 */
+static int
+lkpi_sta_scan_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ /* lkpi_iv_newstate() handles the stop scan case in common code. */
+ return (lkpi_sta_state_do_nada(vap, nstate, arg));
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+static int
+lkpi_sta_auth_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ int error;
+
+ error = lkpi_sta_auth_to_scan(vap, nstate, arg);
+ if (error == 0)
+ error = lkpi_sta_scan_to_init(vap, nstate, arg);
+ return (error);
+}
+
+/* auth_to_auth, assoc_to_assoc. */
+static int
+lkpi_sta_a_to_a(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ struct lkpi_hw *lhw;
+ struct ieee80211_hw *hw;
+ struct lkpi_vif *lvif;
+ struct ieee80211_vif *vif;
+ struct lkpi_sta *lsta;
+ struct ieee80211_prep_tx_info prep_tx_info;
+ int error;
+
+ lhw = vap->iv_ic->ic_softc;
+ hw = LHW_TO_HW(lhw);
+ lvif = VAP_TO_LVIF(vap);
+ vif = LVIF_TO_VIF(lvif);
+
+ IEEE80211_UNLOCK(vap->iv_ic);
+ wiphy_lock(hw->wiphy);
+
+ LKPI_80211_LVIF_LOCK(lvif);
+ /* XXX-BZ KASSERT later? */
+ if (!lvif->lvif_bss_synched || lvif->lvif_bss == NULL) {
+#ifdef LINUXKPI_DEBUG_80211
+ ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p "
+ "lvif_bss->ni %p synched %d\n", __func__, __LINE__,
+ lvif, vap, vap->iv_bss, lvif->lvif_bss,
+ (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
+ lvif->lvif_bss_synched);
+#endif
+ LKPI_80211_LVIF_UNLOCK(lvif);
+ error = ENOTRECOVERABLE;
+ goto out;
+ }
+ lsta = lvif->lvif_bss;
+ LKPI_80211_LVIF_UNLOCK(lvif);
+
+ KASSERT(lsta != NULL, ("%s: lsta %p! lvif %p vap %p\n", __func__,
+ lsta, lvif, vap));
+
+ IMPROVE("event callback?");
+
+ /* End mgd_complete_tx. */
+ if (lsta->in_mgd) {
+ memset(&prep_tx_info, 0, sizeof(prep_tx_info));
+ if (vap->iv_state == IEEE80211_S_AUTH)
+ prep_tx_info.subtype = IEEE80211_STYPE_AUTH;
+ else
+ prep_tx_info.subtype = IEEE80211_STYPE_ASSOC_REQ;
+ prep_tx_info.link_id = 0;
+ lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info);
+ lsta->in_mgd = false;
+ }
+
+ /* Now start auth/assoc. */
+
+ /* Start mgd_prepare_tx. */
+ if (!lsta->in_mgd) {
+ memset(&prep_tx_info, 0, sizeof(prep_tx_info));
+ if (nstate == IEEE80211_S_AUTH)
+ prep_tx_info.subtype = IEEE80211_STYPE_AUTH;
+ else
+ prep_tx_info.subtype = IEEE80211_STYPE_ASSOC_REQ;
+ prep_tx_info.link_id = 0;
+ lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info);
+ lsta->in_mgd = true;
+ }
+
+ error = 0;
+out:
+ wiphy_unlock(hw->wiphy);
+ IEEE80211_LOCK(vap->iv_ic);
+
+ return (error);
+}
+
+static int
+lkpi_sta_assoc_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ int error;
+
+ error = lkpi_sta_assoc_to_auth(vap, nstate, arg);
+ if (error != 0 && error != EALREADY)
+ return (error);
+
+ error = lkpi_sta_auth_to_scan(vap, nstate, arg);
+ return (error);
+}
+
+static int
+lkpi_sta_assoc_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ int error;
+
+ error = lkpi_sta_assoc_to_scan(vap, nstate, arg);
+ if (error != 0 && error != EALREADY)
+ return (error);
+
+ error = lkpi_sta_scan_to_init(vap, nstate, arg); /* do_nada */
+ return (error);
+}
+
+static int
+lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ int error;
+
+ error = lkpi_sta_run_to_assoc(vap, nstate, arg);
+ if (error != 0 && error != EALREADY)
+ return (error);
+
+ error = lkpi_sta_assoc_to_init(vap, nstate, arg);
return (error);
}
static int
lkpi_sta_run_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
+ int error;
+
+ error = lkpi_sta_run_to_assoc(vap, nstate, arg);
+ if (error != 0 && error != EALREADY)
+ return (error);
- return (lkpi_sta_run_to_init(vap, nstate, arg));
+ error = lkpi_sta_assoc_to_scan(vap, nstate, arg);
+ return (error);
}
static int
@@ -3664,13 +3685,11 @@ lkpi_sta_run_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int
{
int error;
- error = lkpi_sta_run_to_init(vap, nstate, arg);
+ error = lkpi_sta_run_to_assoc(vap, nstate, arg);
if (error != 0 && error != EALREADY)
return (error);
- /* At this point iv_bss is long a new node! */
-
- error |= lkpi_sta_scan_to_auth(vap, nstate, 0);
+ error = lkpi_sta_assoc_to_auth(vap, nstate, arg);
return (error);
}
@@ -3688,29 +3707,29 @@ struct fsm_state {
int (*handler)(struct ieee80211vap *, enum ieee80211_state, int);
} sta_state_fsm[] = {
{ IEEE80211_S_INIT, IEEE80211_S_INIT, lkpi_sta_state_do_nada },
- { IEEE80211_S_SCAN, IEEE80211_S_INIT, lkpi_sta_state_do_nada }, /* scan_to_init */
+ { IEEE80211_S_SCAN, IEEE80211_S_INIT, lkpi_sta_state_do_nada }, /* DOWN4 scan_to_init */
{ IEEE80211_S_AUTH, IEEE80211_S_INIT, lkpi_sta_auth_to_init }, /* not explicitly in sta_newstate() */
{ IEEE80211_S_ASSOC, IEEE80211_S_INIT, lkpi_sta_assoc_to_init }, /* Send DEAUTH. */
{ IEEE80211_S_RUN, IEEE80211_S_INIT, lkpi_sta_run_to_init }, /* Send DISASSOC. */
- { IEEE80211_S_INIT, IEEE80211_S_SCAN, lkpi_sta_state_do_nada },
+ { IEEE80211_S_INIT, IEEE80211_S_SCAN, lkpi_sta_init_to_scan }, /* UP1 */
{ IEEE80211_S_SCAN, IEEE80211_S_SCAN, lkpi_sta_state_do_nada },
- { IEEE80211_S_AUTH, IEEE80211_S_SCAN, lkpi_sta_auth_to_scan },
+ { IEEE80211_S_AUTH, IEEE80211_S_SCAN, lkpi_sta_auth_to_scan }, /* DOWN3 */
{ IEEE80211_S_ASSOC, IEEE80211_S_SCAN, lkpi_sta_assoc_to_scan },
{ IEEE80211_S_RUN, IEEE80211_S_SCAN, lkpi_sta_run_to_scan }, /* Beacon miss. */
{ IEEE80211_S_INIT, IEEE80211_S_AUTH, lkpi_sta_scan_to_auth }, /* Send AUTH. */
- { IEEE80211_S_SCAN, IEEE80211_S_AUTH, lkpi_sta_scan_to_auth }, /* Send AUTH. */
+ { IEEE80211_S_SCAN, IEEE80211_S_AUTH, lkpi_sta_scan_to_auth }, /* UP2 Send AUTH. */
{ IEEE80211_S_AUTH, IEEE80211_S_AUTH, lkpi_sta_a_to_a }, /* Send ?AUTH. */
- { IEEE80211_S_ASSOC, IEEE80211_S_AUTH, lkpi_sta_assoc_to_auth }, /* Send ?AUTH. */
+ { IEEE80211_S_ASSOC, IEEE80211_S_AUTH, lkpi_sta_assoc_to_auth }, /* DOWN2 Send ?AUTH. */
{ IEEE80211_S_RUN, IEEE80211_S_AUTH, lkpi_sta_run_to_auth }, /* Send ?AUTH. */
- { IEEE80211_S_AUTH, IEEE80211_S_ASSOC, lkpi_sta_auth_to_assoc }, /* Send ASSOCREQ. */
+ { IEEE80211_S_AUTH, IEEE80211_S_ASSOC, lkpi_sta_auth_to_assoc }, /* UP3.1 Send ASSOCREQ. */
{ IEEE80211_S_ASSOC, IEEE80211_S_ASSOC, lkpi_sta_a_to_a }, /* Send ASSOCREQ. */
- { IEEE80211_S_RUN, IEEE80211_S_ASSOC, lkpi_sta_run_to_assoc }, /* Send ASSOCREQ/REASSOCREQ. */
+ { IEEE80211_S_RUN, IEEE80211_S_ASSOC, lkpi_sta_run_to_assoc }, /* DOWN1 Send ASSOCREQ/REASSOCREQ. */
- { IEEE80211_S_AUTH, IEEE80211_S_RUN, lkpi_sta_auth_to_run },
- { IEEE80211_S_ASSOC, IEEE80211_S_RUN, lkpi_sta_assoc_to_run },
+ { IEEE80211_S_AUTH, IEEE80211_S_RUN, lkpi_sta_auth_to_run }, /* UP3.2 */
+ { IEEE80211_S_ASSOC, IEEE80211_S_RUN, lkpi_sta_assoc_to_run }, /* UP4 */
{ IEEE80211_S_RUN, IEEE80211_S_RUN, lkpi_sta_state_do_nada },
/* Dummy at the end without handler. */
@@ -3752,7 +3771,7 @@ lkpi_iv_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
} else {
ic_printf(vap->iv_ic, "%s: only station mode currently supported: "
- "cap %p iv_opmode %d\n", __func__, vap, vap->iv_opmode);
+ "vap %p iv_opmode %d\n", __func__, vap, vap->iv_opmode);
return (ENOSYS);
}
@@ -3846,7 +3865,7 @@ lkpi_wme_update(struct lkpi_hw *lhw, struct ieee80211vap *vap, bool planned)
struct chanAccParams chp;
struct wmeParams wmeparr[WME_NUM_AC];
struct ieee80211_tx_queue_params txqp;
- enum ieee80211_bss_changed changed;
+ enum ieee80211_bss_changed bss_changed;
int error;
uint16_t ac;
@@ -3897,11 +3916,11 @@ lkpi_wme_update(struct lkpi_hw *lhw, struct ieee80211vap *vap, bool planned)
ic_printf(ic, "%s: conf_tx ac %u failed %d\n",
__func__, ac, error);
}
- changed = BSS_CHANGED_QOS;
+ bss_changed = BSS_CHANGED_QOS;
if (!planned)
- lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, changed);
+ lkpi_bss_info_change(hw, vif, bss_changed);
- return (changed);
+ return (bss_changed);
}
#endif
@@ -3967,7 +3986,7 @@ lkpi_iv_sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
* locking, see if queue_work() is fast enough.
*/
bss_changed = lkpi_update_dtim_tsf(vif, ni, ni->ni_vap, __func__, __LINE__);
- lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
+ lkpi_bss_info_change(hw, vif, bss_changed);
}
/*
@@ -4013,7 +4032,7 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
struct ieee80211vap *vap;
struct ieee80211_vif *vif;
struct ieee80211_tx_queue_params txqp;
- enum ieee80211_bss_changed changed;
+ enum ieee80211_bss_changed bss_changed;
struct sysctl_oid *node;
size_t len;
int error, i;
@@ -4046,6 +4065,10 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
/* Need to fill in other fields as well. */
IMPROVE();
+ /* Create a chanctx to be used later. */
+ IMPROVE("lkpi_alloc_lchanctx reserved as many as can be");
+ (void) lkpi_find_lchanctx_reserved(hw, lvif);
+
/* XXX-BZ hardcoded for now! */
#if 1
RCU_INIT_POINTER(vif->bss_conf.chanctx_conf, NULL);
@@ -4055,12 +4078,14 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
lvif->lvif_ifllevent = EVENTHANDLER_REGISTER(iflladdr_event,
lkpi_vif_iflladdr, vif, EVENTHANDLER_PRI_ANY);
vif->bss_conf.link_id = 0; /* Non-MLO operation. */
+ vif->bss_conf.chanreq.oper.chan = lhw->dflt_chandef.chan;
vif->bss_conf.chanreq.oper.width = NL80211_CHAN_WIDTH_20_NOHT;
vif->bss_conf.use_short_preamble = false; /* vap->iv_flags IEEE80211_F_SHPREAMBLE */
vif->bss_conf.use_short_slot = false; /* vap->iv_flags IEEE80211_F_SHSLOT */
vif->bss_conf.qos = false;
vif->bss_conf.use_cts_prot = false; /* vap->iv_protmode */
vif->bss_conf.ht_operation_mode = IEEE80211_HT_OP_MODE_PROTECTION_NONE;
+ IEEE80211_ADDR_COPY(vif->cfg.ap_addr, ieee80211broadcastaddr);
vif->cfg.aid = 0;
vif->cfg.assoc = false;
vif->cfg.idle = true;
@@ -4130,8 +4155,8 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
LKPI_80211_LHW_LVIF_UNLOCK(lhw);
/* Set bss_info. */
- changed = 0;
- lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, changed);
+ bss_changed = 0;
+ lkpi_bss_info_change(hw, vif, bss_changed);
/* Configure tx queues (conf_tx), default WME & send BSS_CHANGED_QOS. */
IMPROVE("Hardcoded values; to fix see 802.11-2016, 9.4.2.29 EDCA Parameter Set element");
@@ -4149,8 +4174,8 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
__func__, ac, error);
}
wiphy_unlock(hw->wiphy);
- changed = BSS_CHANGED_QOS;
- lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, changed);
+ bss_changed = BSS_CHANGED_QOS;
+ lkpi_bss_info_change(hw, vif, bss_changed);
/* Force MC init. */
lkpi_update_mcast_filter(ic);
@@ -4201,7 +4226,6 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
if (hw->max_listen_interval == 0)
hw->max_listen_interval = 7 * (ic->ic_lintval / ic->ic_bintval);
hw->conf.listen_interval = hw->max_listen_interval;
- ic->ic_set_channel(ic);
/* XXX-BZ do we need to be able to update these? */
hw->wiphy->frag_threshold = vap->iv_fragthreshold;
@@ -5083,6 +5107,9 @@ lkpi_ic_scan_end(struct ieee80211com *ic)
*/
lkpi_enable_hw_scan(lhw);
+ /* Clear the scanning chandef. */
+ memset(&lhw->scan_chandef, 0, sizeof(lhw->scan_chandef));
+
LKPI_80211_LHW_SCAN_LOCK(lhw);
wakeup(lhw);
LKPI_80211_LHW_SCAN_UNLOCK(lhw);
@@ -5125,6 +5152,25 @@ lkpi_ic_scan_mindwell(struct ieee80211_scan_state *ss)
lhw->ic_scan_mindwell(ss);
}
+struct lkpi_ic_set_channel_iter_arg {
+ struct linuxkpi_ieee80211_channel *chan;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+};
+
+static void
+lkpi_ic_set_channel_chanctx_iterf(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *chanctx_conf, void *arg)
+{
+ struct lkpi_ic_set_channel_iter_arg *chanctx_iter_arg;
+
+ chanctx_iter_arg = arg;
+ if (chanctx_iter_arg->chanctx_conf != NULL)
+ return;
+
+ if (chanctx_iter_arg->chan == chanctx_conf->def.chan)
+ chanctx_iter_arg->chanctx_conf = chanctx_conf;
+}
+
static void
lkpi_ic_set_channel(struct ieee80211com *ic)
{
@@ -5132,64 +5178,149 @@ lkpi_ic_set_channel(struct ieee80211com *ic)
struct ieee80211_hw *hw;
struct ieee80211_channel *c;
struct linuxkpi_ieee80211_channel *chan;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ uint32_t changed;
int error;
- bool hw_scan_running;
-
- lhw = ic->ic_softc;
+ bool hw_scan, scan_running;
- /* If we do not support (*config)() save us the work. */
- if (lhw->ops->config == NULL)
- return;
+ IEEE80211_UNLOCK_ASSERT(ic);
- /* If we have a hw_scan running do not switch channels. */
- LKPI_80211_LHW_SCAN_LOCK(lhw);
- hw_scan_running =
- (lhw->scan_flags & (LKPI_LHW_SCAN_RUNNING|LKPI_LHW_SCAN_HW)) ==
- (LKPI_LHW_SCAN_RUNNING|LKPI_LHW_SCAN_HW);
- LKPI_80211_LHW_SCAN_UNLOCK(lhw);
- if (hw_scan_running)
- return;
+ lhw = ic->ic_softc;
c = ic->ic_curchan;
if (c == NULL || c == IEEE80211_CHAN_ANYC) {
- ic_printf(ic, "%s: c %p ops->config %p\n", __func__,
- c, lhw->ops->config);
+ ic_printf(ic, "%s: Unset channel: c %p, ignoring update\n",
+ __func__, c);
return;
}
chan = lkpi_find_lkpi80211_chan(lhw, c);
if (chan == NULL) {
- ic_printf(ic, "%s: c %p chan %p\n", __func__,
- c, chan);
+ ic_printf(ic, "%s: No channel found for c %p(%d) chan %p\n",
+ __func__, c, c->ic_ieee, chan);
return;
}
- /* XXX max power for scanning? */
- IMPROVE();
+ /*
+ * All net80211 callers call ieee80211_radiotap_chan_change().
+ * That means we have nothing to do ourselves.
+ */
+
+ /* If we have a hw_scan running do not switch channels. */
+ LKPI_80211_LHW_SCAN_LOCK(lhw);
+ scan_running = (lhw->scan_flags & LKPI_LHW_SCAN_RUNNING) != 0;
+ hw_scan = (lhw->scan_flags & LKPI_LHW_SCAN_HW) != 0;
+ LKPI_80211_LHW_SCAN_UNLOCK(lhw);
+ if (scan_running && hw_scan) {
+ TRACE_SCAN(ic, "scan_flags %b chan %d nothing to do.",
+ lhw->scan_flags, LKPI_LHW_SCAN_BITS,
+ c->ic_ieee);
+ /* Let us hope we set tx power levels elsewhere. */
+ return;
+ }
hw = LHW_TO_HW(lhw);
- cfg80211_chandef_create(&hw->conf.chandef, chan,
-#ifdef LKPI_80211_HT
- (ic->ic_flags_ht & IEEE80211_FHT_HT) ? NL80211_CHAN_HT20 :
+ wiphy_lock(hw->wiphy);
+ if (scan_running) {
+ struct ieee80211vap *vap;
+ struct lkpi_vif *lvif;
+ struct ieee80211_vif *vif;
+
+ /*
+ * For now and for scanning just pick the first VIF.
+ * net80211 will need to grow DBDC/link_id support
+ * for us to find the vif/chanctx otherwise.
+ */
+ vap = TAILQ_FIRST(&ic->ic_vaps);
+ lvif = VAP_TO_LVIF(vap);
+ vif = LVIF_TO_VIF(lvif);
+
+ /* We always set the chandef to no-HT for scanning. */
+ cfg80211_chandef_create(&lhw->scan_chandef, chan,
+ NL80211_CHAN_NO_HT);
+#ifdef LINUXKPI_DEBUG_80211
+ if ((linuxkpi_debug_80211 & D80211_CHANDEF) != 0)
+ ic_printf(ic, "%s:%d: initialized lhw->scan_chandef\n",
+ __func__, __LINE__);
#endif
- NL80211_CHAN_NO_HT);
- error = lkpi_80211_mo_config(hw, IEEE80211_CONF_CHANGE_CHANNEL);
- if (error != 0 && error != EOPNOTSUPP) {
- ic_printf(ic, "ERROR: %s: config %#0x returned %d\n",
- __func__, IEEE80211_CONF_CHANGE_CHANNEL, error);
- /* XXX should we unroll to the previous chandef? */
- IMPROVE();
- } else {
- /* Update radiotap channels as well. */
- lhw->rtap_tx.wt_chan_freq = htole16(c->ic_freq);
- lhw->rtap_tx.wt_chan_flags = htole16(c->ic_flags);
- lhw->rtap_rx.wr_chan_freq = htole16(c->ic_freq);
- lhw->rtap_rx.wr_chan_flags = htole16(c->ic_flags);
+ /*
+ * This works for as long as we do not do BGSCANs; otherwise
+ * it'll have to be offchan work.
+ */
+ chanctx_conf = lkpi_get_chanctx_conf(hw, vif);
+ changed = lkpi_init_chanctx_conf(hw, &lhw->scan_chandef, chanctx_conf);
+ error = lkpi_set_chanctx_conf(hw, vif, chanctx_conf, changed, true);
+
+ TRACE_SCAN(ic, "scan_flags %b chan %d ???, error %d",
+ lhw->scan_flags, LKPI_LHW_SCAN_BITS,
+ c->ic_ieee, error);
+
+ IMPROVE("max power for scanning; TODO in lkpi_80211_update_chandef");
+
+ } else if (lhw->emulate_chanctx) {
+ /*
+ * We do not set the channel here for normal chanctx operation.
+ * That's just a setup to fail. scan_to_auth will setup all the
+ * other neccessary options for this to work.
+ */
+ struct lkpi_ic_set_channel_iter_arg chanctx_iter_arg = {
+ .chan = chan,
+ .chanctx_conf = NULL,
+ };
+ struct cfg80211_chan_def chandef;
+
+ lkpi_init_chandef(ic, &chandef, chan, c, false);
+
+ ieee80211_iter_chan_contexts_mtx(hw,
+ lkpi_ic_set_channel_chanctx_iterf, &chanctx_iter_arg);
+
+ if (chanctx_iter_arg.chanctx_conf == NULL) {
+ /* No chanctx found for this channel. */
+ struct ieee80211vap *vap;
+ struct lkpi_vif *lvif;
+ struct ieee80211_vif *vif;
+
+ /*
+ * For now just pick the first VIF.
+ * net80211 will need to grow DBDC/link_id support
+ * for us to find the vif/chanctx otherwise.
+ */
+ vap = TAILQ_FIRST(&ic->ic_vaps);
+ lvif = VAP_TO_LVIF(vap);
+ vif = LVIF_TO_VIF(lvif);
+
+#ifdef LINUXKPI_DEBUG_80211
+ if ((linuxkpi_debug_80211 & D80211_CHANDEF) != 0)
+ ic_printf(ic, "%s:%d: using on stack chandef\n",
+ __func__, __LINE__);
+#endif
+ chanctx_conf = lkpi_get_chanctx_conf(hw, vif);
+ changed = lkpi_init_chanctx_conf(hw, &chandef, chanctx_conf);
+ IMPROVE("update HT, VHT, bw, ...");
+ error = lkpi_set_chanctx_conf(hw, vif, chanctx_conf, changed, true);
+
+ } else {
+ /*
+ * We know we are on the same channel.
+ * Do we really have to reset everything?
+ */
+ IMPROVE("update HT, VHT, bw, ...");
+
+#ifdef LINUXKPI_DEBUG_80211
+ if ((linuxkpi_debug_80211 & D80211_CHANDEF) != 0)
+ ic_printf(ic, "%s:%d: using on stack chandef\n",
+ __func__, __LINE__);
+#endif
+
+ chanctx_conf = chanctx_iter_arg.chanctx_conf;
+ changed = lkpi_init_chanctx_conf(hw, &chandef, chanctx_conf);
+ lkpi_80211_mo_change_chanctx(hw, chanctx_conf, changed);
+ }
}
/* Currently PS is hard coded off! Not sure it belongs here. */
- IMPROVE();
+ IMPROVE("PS");
if (ieee80211_hw_check(hw, SUPPORTS_PS) &&
(hw->conf.flags & IEEE80211_CONF_PS) != 0) {
hw->conf.flags &= ~IEEE80211_CONF_PS;
@@ -5199,6 +5330,8 @@ lkpi_ic_set_channel(struct ieee80211com *ic)
"%d\n", __func__, IEEE80211_CONF_CHANGE_PS,
error);
}
+
+ wiphy_unlock(hw->wiphy);
}
static struct ieee80211_node *
@@ -5697,8 +5830,10 @@ lkpi_80211_txq_tx_one(struct lkpi_sta *lsta, struct mbuf *m)
c = ic->ic_curchan;
info->band = lkpi_net80211_chan_to_nl80211_band(c);
info->hw_queue = vif->hw_queue[ac];
- if (m->m_flags & M_EAPOL)
+ if ((m->m_flags & M_EAPOL) != 0) {
info->control.flags |= IEEE80211_TX_CTRL_PORT_CTRL_PROTO;
+ info->flags |= IEEE80211_TX_CTL_USE_MINRATE; /* mt76 */
+ }
info->control.vif = vif;
/* XXX-BZ info->control.rates */
#ifdef __notyet__
@@ -5762,7 +5897,7 @@ lkpi_80211_txq_tx_one(struct lkpi_sta *lsta, struct mbuf *m)
#endif
LKPI_80211_LTXQ_UNLOCK(ltxq);
wiphy_lock(hw->wiphy);
- lkpi_80211_mo_wake_tx_queue(hw, &ltxq->txq);
+ lkpi_80211_mo_wake_tx_queue(hw, &ltxq->txq, true);
wiphy_unlock(hw->wiphy);
return;
@@ -6509,6 +6644,53 @@ linuxkpi_ieee80211_alloc_hw(size_t priv_len, const struct ieee80211_ops *ops)
struct lkpi_hw *lhw;
struct wiphy *wiphy;
int ac;
+ bool emuchanctx;
+
+ /*
+ * Do certain checks before starting to allocate resources.
+ * Store results in temporary variables.
+ */
+
+ /* ac1d519c01ca introduced emulating chanctx changes. */
+ emuchanctx = false;
+ if (ops->add_chanctx == ieee80211_emulate_add_chanctx &&
+ ops->change_chanctx == ieee80211_emulate_change_chanctx &&
+ ops->remove_chanctx == ieee80211_emulate_remove_chanctx) {
+ /*
+ * If we emulate the chanctx ops, we must not have
+ * assign_vif_chanctx and unassign_vif_chanctx.
+ */
+ if (ops->assign_vif_chanctx != NULL ||
+ ops->unassign_vif_chanctx != NULL) {
+ /* Fail gracefully. */
+ printf("%s: emulate_chanctx but "
+ "assign_vif_chanctx %p != NULL || "
+ "unassign_vif_chanctx %p != NULL\n", __func__,
+ ops->assign_vif_chanctx, ops->unassign_vif_chanctx);
+ return (NULL);
+ }
+ emuchanctx = true;
+ }
+ if (!emuchanctx && (ops->add_chanctx == ieee80211_emulate_add_chanctx ||
+ ops->change_chanctx == ieee80211_emulate_change_chanctx ||
+ ops->remove_chanctx == ieee80211_emulate_remove_chanctx)) {
+ printf("%s: not emulating chanctx changes but emulating "
+ "function set: %d/%d/%d\n", __func__,
+ ops->add_chanctx == ieee80211_emulate_add_chanctx,
+ ops->change_chanctx == ieee80211_emulate_change_chanctx,
+ ops->remove_chanctx == ieee80211_emulate_remove_chanctx);
+ return (NULL);
+ }
+ if (!emuchanctx && (ops->add_chanctx == NULL || ops->change_chanctx == NULL ||
+ ops->remove_chanctx == NULL || ops->assign_vif_chanctx == NULL ||
+ ops->unassign_vif_chanctx == NULL)) {
+ printf("%s: not all functions set for chanctx operations "
+ "(emulating chanctx %d): %p/%p/%p %p/%p\n",
+ __func__, emuchanctx,
+ ops->add_chanctx, ops->change_chanctx, ops->remove_chanctx,
+ ops->assign_vif_chanctx, ops->unassign_vif_chanctx);
+ return (NULL);
+ }
/* Get us and the driver data also allocated. */
wiphy = wiphy_new(&linuxkpi_mac80211cfgops, sizeof(*lhw) + priv_len);
@@ -6533,6 +6715,8 @@ linuxkpi_ieee80211_alloc_hw(size_t priv_len, const struct ieee80211_ops *ops)
/* Chanctx_conf */
INIT_LIST_HEAD(&lhw->lchanctx_list);
+ INIT_LIST_HEAD(&lhw->lchanctx_list_reserved);
+ lhw->emulate_chanctx = emuchanctx;
/* Deferred RX path. */
LKPI_80211_LHW_RXQ_LOCK_INIT(lhw);
@@ -6552,6 +6736,8 @@ linuxkpi_ieee80211_alloc_hw(size_t priv_len, const struct ieee80211_ops *ops)
/* BSD Specific. */
lhw->ic = lkpi_ieee80211_ifalloc();
+ if (lhw->emulate_chanctx)
+ ic_printf(lhw->ic, "Using chanctx emulation.\n");
IMPROVE();
return (hw);
@@ -6607,6 +6793,7 @@ linuxkpi_ieee80211_iffree(struct ieee80211_hw *hw)
__func__, lhw, mbufq_len(&lhw->rxq)));
LKPI_80211_LHW_RXQ_LOCK_DESTROY(lhw);
+ wiphy_lock(hw->wiphy);
/* Chanctx_conf. */
if (!list_empty_careful(&lhw->lchanctx_list)) {
struct lkpi_chanctx *lchanctx, *next;
@@ -6619,9 +6806,21 @@ linuxkpi_ieee80211_iffree(struct ieee80211_hw *hw)
lkpi_80211_mo_remove_chanctx(hw, chanctx_conf);
}
list_del(&lchanctx->entry);
+ list_add_rcu(&lchanctx->entry, &lhw->lchanctx_list_reserved);
+ }
+ }
+ if (!list_empty_careful(&lhw->lchanctx_list_reserved)) {
+ struct lkpi_chanctx *lchanctx, *next;
+
+ list_for_each_entry_safe(lchanctx, next, &lhw->lchanctx_list_reserved, entry) {
+ list_del(&lchanctx->entry);
+ if (lchanctx->added_to_drv)
+ panic("%s: lchanctx %p on reserved list still added_to_drv\n",
+ __func__, lchanctx);
free(lchanctx, M_LKPI80211);
}
}
+ wiphy_unlock(hw->wiphy);
LKPI_80211_LHW_MC_LOCK(lhw);
lkpi_cleanup_mcast_list_locked(lhw);
@@ -6922,6 +7121,13 @@ linuxkpi_ieee80211_ifattach(struct ieee80211_hw *hw)
(ic->ic_flags_ht & IEEE80211_FHT_HT) ? NL80211_CHAN_HT20 :
#endif
NL80211_CHAN_NO_HT);
+ lhw->dflt_chandef = hw->conf.chandef;
+#ifdef LINUXKPI_DEBUG_80211
+ if ((linuxkpi_debug_80211 & D80211_CHANDEF) != 0)
+ ic_printf(ic, "%s:%d: initialized "
+ "hw->conf.chandef and dflt_chandef to %p\n",
+ __func__, __LINE__, &lhw->dflt_chandef);
+#endif
break;
}
}
@@ -6972,7 +7178,9 @@ linuxkpi_ieee80211_ifattach(struct ieee80211_hw *hw)
}
if (bootverbose) {
- ic_printf(ic, "netdev_features %b\n", hw->netdev_features, NETIF_F_BITS);
+ if (hw->netdev_features != 0)
+ ic_printf(ic, "netdev_features %b\n",
+ hw->netdev_features, NETIF_F_BITS);
ieee80211_announce(ic);
}
@@ -7068,6 +7276,14 @@ lkpi_ieee80211_iterate_keys(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *, struct ieee80211_key_conf *, void *),
void *arg)
{
+#ifdef LINUXKPI_DEBUG_80211
+ if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO)
+ net80211_vap_printf(LVIF_TO_VAP(VIF_TO_LVIF(vif)),
+ "%s:%d: lsta %6D added_to_drv %d kc[keyix %u] %p\n",
+ __func__, __LINE__, LSTA_TO_STA(lsta)->addr, ":",
+ lsta->added_to_drv, keyix, lsta->kc[keyix]);
+#endif
+
if (!lsta->added_to_drv)
return;
@@ -7711,13 +7927,13 @@ no_trace_beacons:
struct ieee80211_vif *vif;
struct ieee80211_frame *wh;
- wh = mtod(m, struct ieee80211_frame *);
- if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid))
- goto skip_device_ts;
-
lvif = VAP_TO_LVIF(vap);
vif = LVIF_TO_VIF(lvif);
+ wh = mtod(m, struct ieee80211_frame *);
+ if (!IEEE80211_ADDR_EQ(wh->i_addr2, vif->cfg.ap_addr))
+ goto skip_device_ts;
+
IMPROVE("TIMING_BEACON_ONLY?");
/* mac80211 specific (not net80211) so keep it here. */
vif->bss_conf.sync_device_ts = rx_status->device_timestamp;
@@ -8300,6 +8516,9 @@ _lkpi_ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb,
struct ieee80211_node *ni;
struct mbuf *m;
+ if (skb == NULL)
+ return;
+
m = skb->m;
skb->m = NULL;
@@ -8325,13 +8544,13 @@ linuxkpi_ieee80211_tx_status_ext(struct ieee80211_hw *hw,
struct ieee80211_tx_status *txstat)
{
struct sk_buff *skb;
- struct ieee80211_tx_info *info;
+ struct ieee80211_tx_info *info, _info = { };
struct ieee80211_ratectl_tx_status txs;
struct ieee80211_node *ni;
int status;
skb = txstat->skb;
- if (skb->m != NULL) {
+ if (skb != NULL && skb->m != NULL) {
struct mbuf *m;
m = skb->m;
@@ -8341,7 +8560,13 @@ linuxkpi_ieee80211_tx_status_ext(struct ieee80211_hw *hw,
ni = NULL;
}
+ /*
+ * If we have no info information on tx, set info to an all-zero struct
+ * to make the code (and debug output) simpler.
+ */
info = txstat->info;
+ if (info == NULL)
+ info = &_info;
if (info->flags & IEEE80211_TX_STAT_ACK) {
status = 0; /* No error. */
txs.status = IEEE80211_RATECTL_TX_SUCCESS;
@@ -8406,7 +8631,8 @@ linuxkpi_ieee80211_tx_status_ext(struct ieee80211_hw *hw,
if (txstat->free_list) {
_lkpi_ieee80211_free_txskb(hw, skb, status);
- list_add_tail(&skb->list, txstat->free_list);
+ if (skb != NULL)
+ list_add_tail(&skb->list, txstat->free_list);
} else {
linuxkpi_ieee80211_free_txskb(hw, skb, status);
}
@@ -8545,8 +8771,6 @@ struct sk_buff *
linuxkpi_ieee80211_nullfunc_get(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, int linkid, bool qos)
{
- struct lkpi_vif *lvif;
- struct ieee80211vap *vap;
struct sk_buff *skb;
struct ieee80211_frame *nullf;
@@ -8558,17 +8782,15 @@ linuxkpi_ieee80211_nullfunc_get(struct ieee80211_hw *hw,
skb_reserve(skb, hw->extra_tx_headroom);
- lvif = VIF_TO_LVIF(vif);
- vap = LVIF_TO_VAP(lvif);
-
nullf = skb_put_zero(skb, sizeof(*nullf));
nullf->i_fc[0] = IEEE80211_FC0_VERSION_0;
nullf->i_fc[0] |= IEEE80211_FC0_SUBTYPE_NODATA | IEEE80211_FC0_TYPE_DATA;
nullf->i_fc[1] = IEEE80211_FC1_DIR_TODS;
- IEEE80211_ADDR_COPY(nullf->i_addr1, vap->iv_bss->ni_bssid);
+ /* XXX-BZ if link is given, this is different. */
+ IEEE80211_ADDR_COPY(nullf->i_addr1, vif->cfg.ap_addr);
IEEE80211_ADDR_COPY(nullf->i_addr2, vif->addr);
- IEEE80211_ADDR_COPY(nullf->i_addr3, vap->iv_bss->ni_macaddr);
+ IEEE80211_ADDR_COPY(nullf->i_addr3, vif->cfg.ap_addr);
return (skb);
}
@@ -8747,7 +8969,7 @@ lkpi_ieee80211_wake_queues(struct ieee80211_hw *hw, int hwq)
ltxq->stopped = false;
if (!skb_queue_empty(&ltxq->skbq))
- lkpi_80211_mo_wake_tx_queue(hw, sta->txq[tid]);
+ lkpi_80211_mo_wake_tx_queue(hw, sta->txq[tid], false);
}
}
rcu_read_unlock();
@@ -8796,6 +9018,43 @@ linuxkpi_ieee80211_wake_queue(struct ieee80211_hw *hw, int qnum)
spin_unlock_irqrestore(&lhw->txq_lock, flags);
}
+void
+linuxkpi_ieee80211_handle_wake_tx_queue(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq)
+{
+ struct lkpi_hw *lhw;
+
+ lhw = HW_TO_LHW(hw);
+
+ LKPI_80211_LHW_TXQ_LOCK(lhw);
+ ieee80211_txq_schedule_start(hw, txq->ac);
+ do {
+ struct lkpi_txq *ltxq;
+ struct ieee80211_txq *ntxq;
+ struct ieee80211_tx_control control;
+ struct sk_buff *skb;
+
+ ntxq = ieee80211_next_txq(hw, txq->ac);
+ if (ntxq == NULL)
+ break;
+ ltxq = TXQ_TO_LTXQ(ntxq);
+
+ memset(&control, 0, sizeof(control));
+ control.sta = ntxq->sta;
+ do {
+ skb = linuxkpi_ieee80211_tx_dequeue(hw, ntxq);
+ if (skb == NULL)
+ break;
+ ltxq->frms_tx++;
+ lkpi_80211_mo_tx(hw, &control, skb);
+ } while(1);
+
+ ieee80211_return_txq(hw, ntxq, false);
+ } while (1);
+ ieee80211_txq_schedule_end(hw, txq->ac);
+ LKPI_80211_LHW_TXQ_UNLOCK(lhw);
+}
+
/* -------------------------------------------------------------------------- */
/* This is just hardware queues. */
@@ -8897,45 +9156,6 @@ out:
/* -------------------------------------------------------------------------- */
-void
-linuxkpi_ieee80211_handle_wake_tx_queue(struct ieee80211_hw *hw,
- struct ieee80211_txq *txq)
-{
- struct lkpi_hw *lhw;
-
- lhw = HW_TO_LHW(hw);
-
- LKPI_80211_LHW_TXQ_LOCK(lhw);
- ieee80211_txq_schedule_start(hw, txq->ac);
- do {
- struct lkpi_txq *ltxq;
- struct ieee80211_txq *ntxq;
- struct ieee80211_tx_control control;
- struct sk_buff *skb;
-
- ntxq = ieee80211_next_txq(hw, txq->ac);
- if (ntxq == NULL)
- break;
- ltxq = TXQ_TO_LTXQ(ntxq);
-
- memset(&control, 0, sizeof(control));
- control.sta = ntxq->sta;
- do {
- skb = linuxkpi_ieee80211_tx_dequeue(hw, ntxq);
- if (skb == NULL)
- break;
- ltxq->frms_tx++;
- lkpi_80211_mo_tx(hw, &control, skb);
- } while(1);
-
- ieee80211_return_txq(hw, ntxq, false);
- } while (1);
- ieee80211_txq_schedule_end(hw, txq->ac);
- LKPI_80211_LHW_TXQ_UNLOCK(lhw);
-}
-
-/* -------------------------------------------------------------------------- */
-
struct lkpi_cfg80211_bss {
u_int refcnt;
struct cfg80211_bss bss;
@@ -9103,6 +9323,35 @@ linuxkpi_cfg80211_bss_flush(struct wiphy *wiphy)
/* -------------------------------------------------------------------------- */
+static bool
+cfg80211_chan_def_are_same(struct cfg80211_chan_def *cd1,
+ struct cfg80211_chan_def *cd2)
+{
+
+ if (cd1 == cd2)
+ return (true);
+
+ if (cd1 == NULL || cd2 == NULL)
+ return (false);
+
+ if (cd1->chan != cd2->chan)
+ return (false);
+
+ if (cd1->width != cd2->width)
+ return (false);
+
+ if (cd1->center_freq1 != cd2->center_freq1)
+ return (false);
+
+ if (cd1->center_freq2 != cd2->center_freq2)
+ return (false);
+
+ if (cd1->punctured != cd2->punctured)
+ return (false);
+
+ return (true);
+}
+
/*
* hw->conf get initialized/set in various places for us:
* - linuxkpi_ieee80211_alloc_hw(): flags
@@ -9111,26 +9360,80 @@ linuxkpi_cfg80211_bss_flush(struct wiphy *wiphy)
* - lkpi_ic_set_channel(): chandef, flags
*/
-int lkpi_80211_update_chandef(struct ieee80211_hw *hw,
+static int
+lkpi_80211_update_chandef(struct ieee80211_hw *hw,
struct ieee80211_chanctx_conf *new)
{
+ struct lkpi_hw *lhw;
struct cfg80211_chan_def *cd;
uint32_t changed;
int error;
+ bool same;
- changed = 0;
- if (new == NULL || new->def.chan == NULL)
- cd = NULL;
- else
+ lockdep_assert_wiphy(hw->wiphy);
+
+ lhw = HW_TO_LHW(hw);
+ if (!lhw->emulate_chanctx)
+ return (0);
+
+ if (new == NULL || new->def.chan == NULL) {
+ /*
+ * In case of remove "new" is NULL, we need to get us to some
+ * basic channel width but we'd also need to set the channel
+ * accordingly somewhere.
+ * The same is true if we are scanning in which case the
+ * scan_chandef should have a channel set.
+ */
+ if (lhw->scan_chandef.chan != NULL) {
+#ifdef LINUXKPI_DEBUG_80211
+ if ((linuxkpi_debug_80211 & D80211_CHANDEF) != 0)
+ ic_printf(lhw->ic, "%s:%d: using scan_chandef %p\n",
+ __func__, __LINE__, &lhw->scan_chandef);
+#endif
+ cd = &lhw->scan_chandef;
+ } else {
+#ifdef LINUXKPI_DEBUG_80211
+ if ((linuxkpi_debug_80211 & D80211_CHANDEF) != 0)
+ ic_printf(lhw->ic, "%s:%d: using dflt_chandef %p\n",
+ __func__, __LINE__, &lhw->dflt_chandef);
+#endif
+ cd = &lhw->dflt_chandef;
+ }
+ } else {
+#ifdef LINUXKPI_DEBUG_80211
+ if ((linuxkpi_debug_80211 & D80211_CHANDEF) != 0)
+ ic_printf(lhw->ic, "%s:%d: using chanctx %p chandef %p\n",
+ __func__, __LINE__, new, &new->def);
+#endif
cd = &new->def;
+ }
- if (cd && cd->chan != hw->conf.chandef.chan) {
+ changed = 0;
+ same = cfg80211_chan_def_are_same(cd, &hw->conf.chandef);
+ if (!same) {
/* Copy; the chan pointer is fine and will stay valid. */
hw->conf.chandef = *cd;
changed |= IEEE80211_CONF_CHANGE_CHANNEL;
}
IMPROVE("IEEE80211_CONF_CHANGE_PS, IEEE80211_CONF_CHANGE_POWER");
+#ifdef LINUXKPI_DEBUG_80211
+ if ((linuxkpi_debug_80211 & D80211_CHANDEF) != 0)
+ ic_printf(lhw->ic, "%s:%d: chanctx %p { %u } cd %p { %u } "
+ "hw->conf.chandef %p { %u %d %u %u %u }, "
+ "changed %#04x same %d\n",
+ __func__, __LINE__,
+ new, (new != NULL && new->def.chan != NULL) ?
+ new->def.chan->center_freq : 0,
+ cd, cd->chan->center_freq,
+ &hw->conf.chandef, hw->conf.chandef.chan->center_freq,
+ hw->conf.chandef.width,
+ hw->conf.chandef.center_freq1,
+ hw->conf.chandef.center_freq2,
+ hw->conf.chandef.punctured,
+ changed, same);
+#endif
+
if (changed == 0)
return (0);
@@ -9138,6 +9441,97 @@ int lkpi_80211_update_chandef(struct ieee80211_hw *hw,
return (error);
}
+int
+ieee80211_emulate_add_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *chanctx_conf)
+{
+ int error;
+
+ lockdep_assert_wiphy(hw->wiphy);
+
+#ifdef LINUXKPI_DEBUG_80211
+ if ((linuxkpi_debug_80211 & D80211_TRACE) != 0) {
+ struct lkpi_hw *lhw;
+
+ lhw = HW_TO_LHW(hw);
+ ic_printf(lhw->ic, "%s:%d: chanctx_conf %p\n",
+ __func__, __LINE__, chanctx_conf);
+ }
+#endif
+
+ hw->conf.radar_enabled = chanctx_conf->radar_enabled;
+ error = lkpi_80211_update_chandef(hw, chanctx_conf);
+ return (error);
+}
+
+void
+ieee80211_emulate_remove_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *chanctx_conf __unused)
+{
+
+ lockdep_assert_wiphy(hw->wiphy);
+
+#ifdef LINUXKPI_DEBUG_80211
+ if ((linuxkpi_debug_80211 & D80211_TRACE) != 0) {
+ struct lkpi_hw *lhw;
+
+ lhw = HW_TO_LHW(hw);
+ ic_printf(lhw->ic, "%s:%d: chanctx_conf %p\n",
+ __func__, __LINE__, chanctx_conf);
+ }
+#endif
+
+ hw->conf.radar_enabled = false;
+ lkpi_80211_update_chandef(hw, NULL);
+}
+
+void
+ieee80211_emulate_change_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *chanctx_conf, uint32_t changed __unused)
+{
+
+ lockdep_assert_wiphy(hw->wiphy);
+
+#ifdef LINUXKPI_DEBUG_80211
+ if ((linuxkpi_debug_80211 & D80211_TRACE) != 0) {
+ struct lkpi_hw *lhw;
+
+ lhw = HW_TO_LHW(hw);
+ ic_printf(lhw->ic, "%s:%d: chanctx_conf %p\n",
+ __func__, __LINE__, chanctx_conf);
+ }
+#endif
+
+ hw->conf.radar_enabled = chanctx_conf->radar_enabled;
+ lkpi_80211_update_chandef(hw, chanctx_conf);
+}
+
+int
+ieee80211_emulate_switch_vif_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_vif_chanctx_switch *vifs, int n_vifs,
+ enum ieee80211_chanctx_switch_mode mode __unused)
+{
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ int error;
+
+ lockdep_assert_wiphy(hw->wiphy);
+
+ /* Sanity check. */
+ if (n_vifs <= 0)
+ return (-EINVAL);
+ if (vifs == NULL || vifs[0].new_ctx == NULL)
+ return (-EINVAL);
+
+ /*
+ * What to do if n_vifs > 1?
+ * Does that make sense for drivers not supporting chanctx?
+ */
+ hw->conf.radar_enabled = vifs[0].new_ctx->radar_enabled;
+ chanctx_conf = vifs[0].new_ctx;
+ error = lkpi_80211_update_chandef(hw, chanctx_conf);
+ return (error);
+}
+
/* -------------------------------------------------------------------------- */
MODULE_VERSION(linuxkpi_wlan, 1);
diff --git a/sys/compat/linuxkpi/common/src/linux_80211.h b/sys/compat/linuxkpi/common/src/linux_80211.h
index d4f18fcafbba..569c4f12f6d6 100644
--- a/sys/compat/linuxkpi/common/src/linux_80211.h
+++ b/sys/compat/linuxkpi/common/src/linux_80211.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2020-2023 The FreeBSD Foundation
+ * Copyright (c) 2020-2026 The FreeBSD Foundation
* Copyright (c) 2020-2021 Bjoern A. Zeeb
*
* This software was developed by Björn Zeeb under sponsorship from
@@ -57,6 +57,7 @@
#define D80211_IMPROVE 0x00000002
#endif
#define D80211_IMPROVE_TXQ 0x00000004
+#define D80211_CHANDEF 0x00000008
#define D80211_TRACE 0x00000010
#define D80211_TRACEOK 0x00000020
#define D80211_SCAN 0x00000040
@@ -250,10 +251,14 @@ struct lkpi_hw { /* name it mac80211_sc? */
struct sx lvif_sx;
struct list_head lchanctx_list;
+ struct list_head lchanctx_list_reserved;
struct netdev_hw_addr_list mc_list;
unsigned int mc_flags;
struct sx mc_sx;
+ struct cfg80211_chan_def dflt_chandef;
+ struct cfg80211_chan_def scan_chandef;
+
struct mtx txq_mtx;
uint32_t txq_generation[IEEE80211_NUM_ACS];
spinlock_t txq_scheduled_lock[IEEE80211_NUM_ACS];
@@ -314,6 +319,7 @@ struct lkpi_hw { /* name it mac80211_sc? */
bool mc_all_multi;
bool update_wme;
bool rxq_stopped;
+ bool emulate_chanctx;
/* Must be last! */
struct ieee80211_hw hw __aligned(CACHE_LINE_SIZE);
@@ -328,6 +334,7 @@ struct lkpi_chanctx {
struct list_head entry;
bool added_to_drv; /* Managed by MO */
+ struct lkpi_vif *lvif; /* Backpointer. */
struct ieee80211_chanctx_conf chanctx_conf __aligned(CACHE_LINE_SIZE);
};
@@ -469,6 +476,10 @@ void lkpi_80211_mo_change_chanctx(struct ieee80211_hw *,
struct ieee80211_chanctx_conf *, uint32_t);
void lkpi_80211_mo_remove_chanctx(struct ieee80211_hw *,
struct ieee80211_chanctx_conf *);
+void lkpi_80211_mo_vif_cfg_changed(struct ieee80211_hw *, struct ieee80211_vif *,
+ uint64_t, bool);
+void lkpi_80211_mo_link_info_changed(struct ieee80211_hw *, struct ieee80211_vif *,
+ struct ieee80211_bss_conf *, uint64_t, uint8_t, bool);
void lkpi_80211_mo_bss_info_changed(struct ieee80211_hw *, struct ieee80211_vif *,
struct ieee80211_bss_conf *, uint64_t);
int lkpi_80211_mo_conf_tx(struct ieee80211_hw *, struct ieee80211_vif *,
@@ -481,7 +492,8 @@ void lkpi_80211_mo_mgd_complete_tx(struct ieee80211_hw *, struct ieee80211_vif *
struct ieee80211_prep_tx_info *);
void lkpi_80211_mo_tx(struct ieee80211_hw *, struct ieee80211_tx_control *,
struct sk_buff *);
-void lkpi_80211_mo_wake_tx_queue(struct ieee80211_hw *, struct ieee80211_txq *);
+void lkpi_80211_mo_wake_tx_queue(struct ieee80211_hw *, struct ieee80211_txq *,
+ bool);
void lkpi_80211_mo_sync_rx_queues(struct ieee80211_hw *);
void lkpi_80211_mo_sta_pre_rcu_remove(struct ieee80211_hw *,
struct ieee80211_vif *, struct ieee80211_sta *);
diff --git a/sys/compat/linuxkpi/common/src/linux_80211_macops.c b/sys/compat/linuxkpi/common/src/linux_80211_macops.c
index a85e6d0d0dd7..42067e36c953 100644
--- a/sys/compat/linuxkpi/common/src/linux_80211_macops.c
+++ b/sys/compat/linuxkpi/common/src/linux_80211_macops.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2021-2022 The FreeBSD Foundation
+ * Copyright (c) 2021-2026 The FreeBSD Foundation
*
* This software was developed by Björn Zeeb under sponsorship from
* the FreeBSD Foundation.
@@ -546,24 +546,80 @@ lkpi_80211_mo_remove_chanctx(struct ieee80211_hw *hw,
}
void
-lkpi_80211_mo_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- struct ieee80211_bss_conf *conf, uint64_t changed)
+lkpi_80211_mo_vif_cfg_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ uint64_t vif_cfg_bits, bool fallback)
{
struct lkpi_hw *lhw;
+ might_sleep();
+ /* XXX-FINISH all callers for lockdep_assert_wiphy(hw->wiphy); */
+
+ lhw = HW_TO_LHW(hw);
+ if (lhw->ops->vif_cfg_changed == NULL &&
+ lhw->ops->bss_info_changed == NULL)
+ return;
+
+ if (vif_cfg_bits == 0)
+ return;
+
+ LKPI_80211_TRACE_MO("hw %p vif %p vif_cfg_bits %#jx", hw, vif, (uintmax_t)vif_cfg_bits);
+ if (lhw->ops->link_info_changed != NULL)
+ lhw->ops->vif_cfg_changed(hw, vif, vif_cfg_bits);
+ else if (fallback)
+ lhw->ops->bss_info_changed(hw, vif, &vif->bss_conf, vif_cfg_bits);
+}
+
+void
+lkpi_80211_mo_link_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *conf, uint64_t link_info_bits, uint8_t link_id,
+ bool fallback)
+{
+ struct lkpi_hw *lhw;
+
+ might_sleep();
+ /* XXX-FINISH all callers for lockdep_assert_wiphy(hw->wiphy); */
+
lhw = HW_TO_LHW(hw);
if (lhw->ops->link_info_changed == NULL &&
lhw->ops->bss_info_changed == NULL)
return;
- if (changed == 0)
+ if (link_info_bits == 0)
return;
- LKPI_80211_TRACE_MO("hw %p vif %p conf %p changed %#jx", hw, vif, conf, (uintmax_t)changed);
+ if (!ieee80211_vif_link_active(vif, link_id))
+ return;
+
+ LKPI_80211_TRACE_MO("hw %p vif %p conf %p link_info_bits %#jx", hw, vif, conf, (uintmax_t)link_info_bits);
if (lhw->ops->link_info_changed != NULL)
- lhw->ops->link_info_changed(hw, vif, conf, changed);
- else
- lhw->ops->bss_info_changed(hw, vif, conf, changed);
+ lhw->ops->link_info_changed(hw, vif, conf, link_info_bits);
+ else if (fallback)
+ lhw->ops->bss_info_changed(hw, vif, conf, link_info_bits);
+}
+
+/*
+ * This is basically obsolete but one caller.
+ * The functionality is now split between lkpi_80211_mo_link_info_changed() and
+ * lkpi_80211_mo_vif_cfg_changed(). Those functions have a flag whether to call
+ * the (*bss_info_changed) fallback or not. See lkpi_bss_info_change().
+ */
+void
+lkpi_80211_mo_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *conf, uint64_t bss_changed)
+{
+ struct lkpi_hw *lhw;
+
+ /* XXX-FINISH all callers for lockdep_assert_wiphy(hw->wiphy); */
+
+ lhw = HW_TO_LHW(hw);
+ if (lhw->ops->bss_info_changed == NULL)
+ return;
+
+ if (bss_changed == 0)
+ return;
+
+ LKPI_80211_TRACE_MO("hw %p vif %p conf %p changed %#jx", hw, vif, conf, (uintmax_t)bss_changed);
+ lhw->ops->bss_info_changed(hw, vif, conf, bss_changed);
}
int
@@ -644,11 +700,17 @@ lkpi_80211_mo_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *txctrl,
}
void
-lkpi_80211_mo_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
+lkpi_80211_mo_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq,
+ bool schedule)
{
struct lkpi_hw *lhw;
lhw = HW_TO_LHW(hw);
+
+ /* Do the schedule before the check for wake_tx_queue supported! */
+ if (schedule)
+ ieee80211_schedule_txq(hw, txq);
+
if (lhw->ops->wake_tx_queue == NULL)
return;
diff --git a/sys/compat/linuxkpi/common/src/linux_compat.c b/sys/compat/linuxkpi/common/src/linux_compat.c
index 8fc644241d79..2fd502990eb3 100644
--- a/sys/compat/linuxkpi/common/src/linux_compat.c
+++ b/sys/compat/linuxkpi/common/src/linux_compat.c
@@ -98,6 +98,7 @@
#include <linux/interval_tree_generic.h>
#include <linux/printk.h>
#include <linux/seq_file.h>
+#include <linux/uuid.h>
#if defined(__i386__) || defined(__amd64__)
#include <asm/smp.h>
@@ -164,6 +165,10 @@ unsigned long linux_timer_hz_mask;
wait_queue_head_t linux_bit_waitq;
wait_queue_head_t linux_var_waitq;
+const guid_t guid_null;
+
+enum system_states system_state = SYSTEM_RUNNING;
+
int
panic_cmp(struct rb_node *one, struct rb_node *two)
{
diff --git a/sys/compat/linuxkpi/common/src/linux_eventfd.c b/sys/compat/linuxkpi/common/src/linux_eventfd.c
new file mode 100644
index 000000000000..126c6c54b9a5
--- /dev/null
+++ b/sys/compat/linuxkpi/common/src/linux_eventfd.c
@@ -0,0 +1,63 @@
+/*-
+ * Copyright (c) 2025 The FreeBSD Foundation
+ * Copyright (c) 2025 Jean-Sébastien Pédron
+ *
+ * This software was developed by Jean-Sébastien Pédron under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * 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/file.h>
+#include <sys/filedesc.h>
+
+#include <linux/eventfd.h>
+
+struct eventfd_ctx *
+lkpi_eventfd_ctx_fdget(int fd)
+{
+ struct file *fp;
+ struct eventfd_ctx *ctx;
+
+ /* Lookup file pointer by file descriptor index. */
+ if (fget_unlocked(curthread, fd, &cap_no_rights, &fp) != 0)
+ return (ERR_PTR(-EBADF));
+
+ /*
+ * eventfd_get() bumps the refcount, so we can safely release the
+ * reference on the file itself afterwards.
+ */
+ ctx = eventfd_get(fp);
+ fdrop(fp, curthread);
+
+ if (ctx == NULL)
+ return (ERR_PTR(-EBADF));
+
+ return (ctx);
+}
+
+void
+lkpi_eventfd_ctx_put(struct eventfd_ctx *ctx)
+{
+ eventfd_put(ctx);
+}
diff --git a/sys/compat/linuxkpi/common/src/linux_netdev.c b/sys/compat/linuxkpi/common/src/linux_netdev.c
index ce9153614104..11bbbfe6e279 100644
--- a/sys/compat/linuxkpi/common/src/linux_netdev.c
+++ b/sys/compat/linuxkpi/common/src/linux_netdev.c
@@ -405,7 +405,7 @@ linuxkpi_alloc_netdev(size_t len, const char *name, uint32_t flags,
/* Always first as it zeros! */
linuxkpi_init_dummy_netdev(ndev);
- strlcpy(ndev->name, name, sizeof(*ndev->name));
+ strlcpy(ndev->name, name, sizeof(ndev->name));
/* This needs extending as we support more. */
diff --git a/sys/compat/linuxkpi/common/src/linux_page.c b/sys/compat/linuxkpi/common/src/linux_page.c
index 82f3a2a4639f..b91115a5ff16 100644
--- a/sys/compat/linuxkpi/common/src/linux_page.c
+++ b/sys/compat/linuxkpi/common/src/linux_page.c
@@ -87,8 +87,7 @@ linux_page_address(const struct page *page)
{
if (page->object != kernel_object) {
- return (PMAP_HAS_DMAP ?
- ((void *)(uintptr_t)PHYS_TO_DMAP(page_to_phys(page))) :
+ return (PMAP_HAS_DMAP ? PHYS_TO_DMAP(page_to_phys(page)) :
NULL);
}
return ((void *)(uintptr_t)(VM_MIN_KERNEL_ADDRESS +
@@ -119,10 +118,19 @@ linux_alloc_pages(gfp_t flags, unsigned int order)
req |= VM_ALLOC_NORECLAIM;
retry:
- page = vm_page_alloc_noobj_contig(req, npages, 0, pmax,
- PAGE_SIZE, 0, VM_MEMATTR_DEFAULT);
+ if ((flags & __GFP_THISNODE) != 0) {
+ int curdomain = PCPU_GET(domain);
+ page = vm_page_alloc_noobj_contig_domain(
+ curdomain, req, npages, 0, pmax,
+ PAGE_SIZE, 0, VM_MEMATTR_DEFAULT);
+ } else {
+ page = vm_page_alloc_noobj_contig(
+ req, npages, 0, pmax,
+ PAGE_SIZE, 0, VM_MEMATTR_DEFAULT);
+ }
+
if (page == NULL) {
- if ((flags & (M_WAITOK | __GFP_NORETRY)) ==
+ if ((flags & (M_WAITOK | __GFP_NORETRY | __GFP_THISNODE)) ==
M_WAITOK) {
int err = vm_page_reclaim_contig(req,
npages, 0, pmax, PAGE_SIZE, 0);
@@ -233,7 +241,7 @@ linux_free_kmem(vm_offset_t addr, unsigned int order)
} else {
vm_page_t page;
- page = PHYS_TO_VM_PAGE(DMAP_TO_PHYS(addr));
+ page = DMAP_TO_VM_PAGE(addr);
linux_free_pages(page, order);
}
}
@@ -415,17 +423,17 @@ iounmap(void *addr)
void *
vmap(struct page **pages, unsigned int count, unsigned long flags, int prot)
{
- vm_offset_t off;
+ void *off;
size_t size;
size = count * PAGE_SIZE;
off = kva_alloc(size);
- if (off == 0)
+ if (off == NULL)
return (NULL);
- vmmap_add((void *)off, size);
+ vmmap_add(off, size);
pmap_qenter(off, pages, count);
- return ((void *)off);
+ return (off);
}
#define VMAP_MAX_CHUNK_SIZE (65536U / sizeof(struct vm_page)) /* KMEM_ZMAX */
@@ -434,7 +442,8 @@ void *
linuxkpi_vmap_pfn(unsigned long *pfns, unsigned int count, int prot)
{
vm_page_t m, *ma, fma;
- vm_offset_t off, coff;
+ void *off;
+ char *coff;
vm_paddr_t pa;
vm_memattr_t attr;
size_t size;
@@ -442,9 +451,9 @@ linuxkpi_vmap_pfn(unsigned long *pfns, unsigned int count, int prot)
size = ptoa(count);
off = kva_alloc(size);
- if (off == 0)
+ if (off == NULL)
return (NULL);
- vmmap_add((void *)off, size);
+ vmmap_add(off, size);
chunk = MIN(count, VMAP_MAX_CHUNK_SIZE);
attr = pgprot2cachemode(prot);
@@ -480,7 +489,7 @@ linuxkpi_vmap_pfn(unsigned long *pfns, unsigned int count, int prot)
free(fma, M_TEMP);
free(ma, M_TEMP);
- return ((void *)off);
+ return (off);
}
void
@@ -491,8 +500,8 @@ vunmap(void *addr)
vmmap = vmmap_remove(addr);
if (vmmap == NULL)
return;
- pmap_qremove((vm_offset_t)addr, vmmap->vm_size / PAGE_SIZE);
- kva_free((vm_offset_t)addr, vmmap->vm_size);
+ pmap_qremove(addr, vmmap->vm_size / PAGE_SIZE);
+ kva_free(addr, vmmap->vm_size);
kfree(vmmap);
}
diff --git a/sys/compat/linuxkpi/common/src/linux_pci.c b/sys/compat/linuxkpi/common/src/linux_pci.c
index df5255d8a6ca..477cb321ea9e 100644
--- a/sys/compat/linuxkpi/common/src/linux_pci.c
+++ b/sys/compat/linuxkpi/common/src/linux_pci.c
@@ -385,14 +385,16 @@ lkpifill_pci_dev(device_t dev, struct pci_dev *pdev)
pdev->dev.bsddev = dev;
pdev->dev.parent = &linux_root_device;
pdev->dev.release = lkpi_pci_dev_release;
- INIT_LIST_HEAD(&pdev->dev.irqents);
if (pci_msi_count(dev) > 0)
pdev->msi_desc = malloc(pci_msi_count(dev) *
sizeof(*pdev->msi_desc), M_DEVBUF, M_WAITOK | M_ZERO);
+ TAILQ_INIT(&pdev->mmio);
+ spin_lock_init(&pdev->pcie_cap_lock);
spin_lock_init(&pdev->dev.devres_lock);
INIT_LIST_HEAD(&pdev->dev.devres_head);
+ INIT_LIST_HEAD(&pdev->dev.irqents);
return (0);
}
@@ -613,9 +615,6 @@ linux_pci_attach_device(device_t dev, struct pci_driver *pdrv,
if (error)
goto out_dma_init;
- TAILQ_INIT(&pdev->mmio);
- spin_lock_init(&pdev->pcie_cap_lock);
-
spin_lock(&pci_lock);
list_add(&pdev->links, &pci_devices);
spin_unlock(&pci_lock);
@@ -1720,9 +1719,26 @@ lkpi_dma_unmap(struct device *dev, dma_addr_t dma_addr, size_t len,
}
LINUX_DMA_PCTRIE_REMOVE(&priv->ptree, dma_addr);
- if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
- dma_sync_single_for_cpu(dev, dma_addr, len, direction);
+ if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) != 0)
+ goto skip_sync;
+ /* dma_sync_single_for_cpu() unrolled to avoid lock recursicn. */
+ switch (direction) {
+ case DMA_BIDIRECTIONAL:
+ bus_dmamap_sync(obj->dmat, obj->dmamap, BUS_DMASYNC_POSTREAD);
+ bus_dmamap_sync(obj->dmat, obj->dmamap, BUS_DMASYNC_PREREAD);
+ break;
+ case DMA_TO_DEVICE:
+ bus_dmamap_sync(obj->dmat, obj->dmamap, BUS_DMASYNC_POSTWRITE);
+ break;
+ case DMA_FROM_DEVICE:
+ bus_dmamap_sync(obj->dmat, obj->dmamap, BUS_DMASYNC_POSTREAD);
+ break;
+ default:
+ break;
+ }
+
+skip_sync:
bus_dmamap_unload(obj->dmat, obj->dmamap);
bus_dmamap_destroy(obj->dmat, obj->dmamap);
DMA_PRIV_UNLOCK(priv);
@@ -1796,6 +1812,42 @@ lkpi_dmam_free_coherent(struct device *dev, void *p)
dma_free_coherent(dev, dr->size, dr->mem, *dr->handle);
}
+static int
+lkpi_dmam_coherent_match(struct device *dev, void *dr, void *mp)
+{
+ struct lkpi_devres_dmam_coherent *a, *b;
+
+ a = dr;
+ b = mp;
+
+ if (a->mem != b->mem)
+ return (0);
+ if (a->size != b->size || a->handle != b->handle)
+ dev_WARN(dev, "for mem %p: size %zu != %zu || handle %#jx != %#jx\n",
+ a->mem, a->size, b->size,
+ (uintmax_t)a->handle, (uintmax_t)b->handle);
+ return (1);
+}
+
+void
+linuxkpi_dmam_free_coherent(struct device *dev, size_t size,
+ void *addr, dma_addr_t dma_handle)
+{
+ struct lkpi_devres_dmam_coherent match = {
+ .size = size,
+ .handle = &dma_handle,
+ .mem = addr
+ };
+ int error;
+
+ error = devres_destroy(dev, lkpi_dmam_free_coherent,
+ lkpi_dmam_coherent_match, &match);
+ if (error != 0)
+ dev_WARN(dev, "devres_destroy returned %d, size %zu addr %p "
+ "dma_handle %#jx\n", error, size, addr, (uintmax_t)dma_handle);
+ dma_free_coherent(dev, size, addr, dma_handle);
+}
+
void *
linuxkpi_dmam_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle,
gfp_t flag)
diff --git a/sys/compat/linuxkpi/common/src/linux_radix.c b/sys/compat/linuxkpi/common/src/linux_radix.c
index ee6b3a63c370..760f583f25c8 100644
--- a/sys/compat/linuxkpi/common/src/linux_radix.c
+++ b/sys/compat/linuxkpi/common/src/linux_radix.c
@@ -52,6 +52,81 @@ radix_pos(long id, int height)
return (id >> (RADIX_TREE_MAP_SHIFT * height)) & RADIX_TREE_MAP_MASK;
}
+static inline int
+root_tag_get(const struct radix_tree_root *root, unsigned tag)
+{
+ return (test_bit(tag, root->tags));
+}
+
+static inline void
+root_tag_set(struct radix_tree_root *root, unsigned tag)
+{
+ set_bit(tag, root->tags);
+}
+
+static inline void
+root_tag_clear(struct radix_tree_root *root, unsigned tag)
+{
+ clear_bit(tag, root->tags);
+}
+
+static inline int
+tag_get(const struct radix_tree_node *node, unsigned int tag, int pos)
+{
+ return (test_bit(pos, node->tags[tag]));
+}
+
+static inline void
+tag_set(struct radix_tree_node *node, unsigned int tag, int pos)
+{
+ set_bit(pos, node->tags[tag]);
+}
+
+static inline void
+tag_clear(struct radix_tree_node *node, unsigned int tag, int pos)
+{
+ clear_bit(pos, node->tags[tag]);
+}
+
+static inline bool
+any_tag_set(const struct radix_tree_node *node, unsigned int tag)
+{
+ unsigned int pos;
+
+ for (pos = 0; pos < RADIX_TREE_TAG_LONGS; pos++)
+ if (node->tags[tag][pos])
+ return (true);
+
+ return (false);
+}
+
+static void
+node_tag_clear(struct radix_tree_root *root, struct radix_tree_node *stack[],
+ unsigned long index, unsigned int tag)
+{
+ struct radix_tree_node *node;
+ int height, pos;
+
+ height = 1;
+ node = stack[height];
+
+ while (node && height <= root->height - 1) {
+ pos = radix_pos(index, height);
+ if (!tag_get(node, tag, pos))
+ return;
+
+ tag_clear(node, tag, pos);
+ if (any_tag_set(node, tag))
+ return;
+
+ height++;
+ node = stack[height];
+ }
+
+ if (root_tag_get(root, tag))
+ root_tag_clear(root, tag);
+}
+
static void
radix_tree_clean_root_node(struct radix_tree_root *root)
{
@@ -84,14 +159,70 @@ out:
return (item);
}
+unsigned int
+radix_tree_gang_lookup(const struct radix_tree_root *root, void **results,
+ unsigned long first_index, unsigned int max_items)
+{
+ struct radix_tree_iter iter;
+ void **slot;
+ int count;
+
+ count = 0;
+ if (max_items == 0)
+ return (count);
+
+ radix_tree_for_each_slot(slot, root, &iter, first_index) {
+ results[count] = *slot;
+ if (results[count] == NULL)
+ continue;
+
+ count++;
+ if (count == max_items)
+ break;
+ }
+
+ return (count);
+}
+
+unsigned int
+radix_tree_gang_lookup_tag(const struct radix_tree_root *root,
+ void **results, unsigned long first_index, unsigned int max_items,
+ unsigned int tag)
+{
+ struct radix_tree_iter iter;
+ void **slot;
+ int count;
+
+ count = 0;
+ if (max_items == 0)
+ return (count);
+
+ radix_tree_for_each_slot_tagged(slot, root, &iter, first_index, tag) {
+ results[count] = *slot;
+ if (results[count] == NULL)
+ continue;
+
+ count++;
+ if (count == max_items)
+ break;
+ }
+
+ return (count);
+}
+
bool
radix_tree_iter_find(const struct radix_tree_root *root,
- struct radix_tree_iter *iter, void ***pppslot)
+ struct radix_tree_iter *iter, void ***pppslot, int flags)
{
struct radix_tree_node *node;
unsigned long index = iter->index;
+ unsigned int tag;
int height;
+ tag = flags & RADIX_TREE_ITER_TAG_MASK;
+ if ((flags & RADIX_TREE_ITER_TAGGED) && !root_tag_get(root, tag))
+ return (false);
+
restart:
node = root->rnode;
if (node == NULL)
@@ -109,7 +240,9 @@ restart:
*pppslot = node->slots + pos;
next = node->slots[pos];
- if (next == NULL) {
+ if (next == NULL ||
+ (flags & RADIX_TREE_ITER_TAGGED &&
+ !tag_get(next, tag, pos))) {
index += step;
index &= -step;
if ((index & mask) == 0)
@@ -131,6 +264,7 @@ radix_tree_delete(struct radix_tree_root *root, unsigned long index)
void *item;
int height;
int idx;
+ int tag;
item = NULL;
node = root->rnode;
@@ -144,9 +278,15 @@ radix_tree_delete(struct radix_tree_root *root, unsigned long index)
stack[height] = node;
node = node->slots[radix_pos(index, height--)];
}
- idx = radix_pos(index, 0);
- if (node)
+
+ if (node) {
+ idx = radix_pos(index, 0);
item = node->slots[idx];
+
+ for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++)
+ node_tag_clear(root, stack, index, tag);
+ }
+
/*
* If we removed something reduce the height of the tree.
*/
@@ -380,3 +520,74 @@ radix_tree_store(struct radix_tree_root *root, unsigned long index, void **ppite
node->count++;
return (0);
}
+
+void *
+radix_tree_tag_set(struct radix_tree_root *root, unsigned long index,
+ unsigned int tag)
+{
+ struct radix_tree_node *node;
+ void *item;
+ int height, pos;
+
+ item = NULL;
+ node = root->rnode;
+ height = root->height - 1;
+ if (index > radix_max(root))
+ goto out;
+
+ while (height) {
+ KASSERT(
+ node != NULL,
+ ("Node at index %lu does not exist in radix tree %p",
+ index, root));
+
+ pos = radix_pos(index, height--);
+ node = node->slots[pos];
+
+ if (!tag_get(node, tag, pos))
+ tag_set(node, tag, pos);
+ }
+
+ item = node->slots[radix_pos(index, 0)];
+
+ root_tag_set(root, tag);
+
+out:
+ return (item);
+}
+
+void *
+radix_tree_tag_clear(struct radix_tree_root *root,
+ unsigned long index, unsigned int tag)
+{
+ struct radix_tree_node *stack[RADIX_TREE_MAX_HEIGHT];
+ struct radix_tree_node *node;
+ void *item;
+ int height;
+
+ item = NULL;
+ node = root->rnode;
+ height = root->height - 1;
+ if (index > radix_max(root))
+ goto out;
+
+ while (height && node) {
+ stack[height] = node;
+ node = node->slots[radix_pos(index, height--)];
+ }
+
+ if (node) {
+ item = node->slots[radix_pos(index, 0)];
+
+ node_tag_clear(root, stack, index, tag);
+ }
+
+out:
+ return (item);
+}
+
+int
+radix_tree_tagged(const struct radix_tree_root *root, unsigned int tag)
+{
+ return (root_tag_get(root, tag));
+}
diff --git a/sys/compat/linuxkpi/common/src/linux_seq_buf.c b/sys/compat/linuxkpi/common/src/linux_seq_buf.c
new file mode 100644
index 000000000000..112c53044c22
--- /dev/null
+++ b/sys/compat/linuxkpi/common/src/linux_seq_buf.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2025-2026 The FreeBSD Foundation
+ * Copyright (c) 2025-2026 Jean-Sébastien Pédron <dumbbell@FreeBSD.org>
+ *
+ * This software was developed by Jean-Sébastien Pédron under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <linux/seq_buf.h>
+
+void
+linuxkpi_seq_buf_init(struct seq_buf *s, char *buf, unsigned int size)
+{
+ s->buffer = buf;
+ s->size = size;
+
+ seq_buf_clear(s);
+}
+
+int
+linuxkpi_seq_buf_printf(struct seq_buf *s, const char *fmt, ...)
+{
+ int ret;
+ va_list args;
+
+ va_start(args, fmt);
+ ret = seq_buf_vprintf(s, fmt, args);
+ va_end(args);
+
+ return (ret);
+}
+
+int
+linuxkpi_seq_buf_vprintf(struct seq_buf *s, const char *fmt, va_list args)
+{
+ int ret;
+
+ if (!seq_buf_has_overflowed(s)) {
+ ret = vsnprintf(s->buffer + s->len, s->size - s->len, fmt, args);
+ if (s->len + ret < s->size) {
+ s->len += ret;
+ return (0);
+ }
+ }
+
+ seq_buf_set_overflow(s);
+ return (-1);
+}
+
+const char *
+linuxkpi_seq_buf_str(struct seq_buf *s)
+{
+ if (s->size == 0)
+ return ("");
+
+ if (seq_buf_buffer_left(s))
+ s->buffer[s->len] = '\0';
+ else
+ s->buffer[s->size - 1] = '\0';
+
+ return (s->buffer);
+}
diff --git a/sys/compat/linuxkpi/common/src/linux_seq_file.c b/sys/compat/linuxkpi/common/src/linux_seq_file.c
index 9c06fe27bebe..efa41b28c6b1 100644
--- a/sys/compat/linuxkpi/common/src/linux_seq_file.c
+++ b/sys/compat/linuxkpi/common/src/linux_seq_file.c
@@ -40,34 +40,78 @@
MALLOC_DEFINE(M_LSEQ, "seq_file", "seq_file");
ssize_t
-seq_read(struct linux_file *f, char *ubuf, size_t size, off_t *ppos)
+seq_read(struct linux_file *f, char __user *ubuf, size_t size, off_t *ppos)
{
struct seq_file *m;
struct sbuf *sbuf;
void *p;
- ssize_t rc;
+ ssize_t rc, oldlen;
+ size_t todo;
m = f->private_data;
sbuf = m->buf;
+ sbuf_clear(sbuf);
p = m->op->start(m, ppos);
- rc = m->op->show(m, p);
- if (rc)
+ if (p == NULL)
+ return (0);
+
+ rc = 0;
+ while (size > sbuf_len(sbuf)) {
+ oldlen = sbuf_len(sbuf);
+ rc = m->op->show(m, p);
+ if (rc < 0)
+ break;
+
+ if (rc == SEQ_SKIP) {
+ /* Discard any data written in show callback. */
+ sbuf_setpos(sbuf, oldlen);
+ rc = 0;
+ }
+
+ /*
+ * If the sbuf has overflowed, bail. Discard any
+ * partial output from the show callback for this item
+ * preserving output from any earlier items. Since we
+ * break before calling the next callback to update
+ * *ppos, a subsequent read() will start by displaying
+ * the current item. However, if the current item
+ * could not be displayed by itself, still fail with
+ * ENOMEM rather than returning EOF.
+ */
+ if (sbuf_error(sbuf)) {
+ if (oldlen != 0)
+ sbuf_setpos(sbuf, oldlen);
+ break;
+ }
+
+ /*
+ * XXX: The seq_file documentation claims that Linux
+ * warns if this callback doesn't update the value in
+ * *ppos. We don't bother warning here.
+ */
+ p = m->op->next(m, p, ppos);
+ if (p == NULL)
+ break;
+ }
+ m->op->stop(m, p);
+
+ if (rc < 0)
return (rc);
rc = sbuf_finish(sbuf);
- if (rc)
- return (rc);
+ if (rc != 0)
+ return (-rc);
- rc = sbuf_len(sbuf);
- if (*ppos >= rc || size < 1)
- return (-EINVAL);
+ todo = sbuf_len(sbuf);
+ if (todo > size)
+ todo = size;
- size = min(rc - *ppos, size);
- memcpy(ubuf, sbuf_data(sbuf) + *ppos, size);
- *ppos += size;
+ rc = copy_to_user(ubuf, sbuf_data(sbuf), todo);
+ if (rc != 0)
+ return (-EFAULT);
- return (size);
+ return (todo);
}
int
diff --git a/sys/compat/linuxkpi/common/src/linux_simple_attr.c b/sys/compat/linuxkpi/common/src/linux_simple_attr.c
index 8cc9ec7ecbc9..e5514194cb33 100644
--- a/sys/compat/linuxkpi/common/src/linux_simple_attr.c
+++ b/sys/compat/linuxkpi/common/src/linux_simple_attr.c
@@ -99,7 +99,8 @@ simple_attr_release(struct inode *inode, struct file *filp)
* On failure, negative signed ERRNO
*/
ssize_t
-simple_attr_read(struct file *filp, char *buf, size_t read_size, loff_t *ppos)
+simple_attr_read(struct file *filp, char __user *buf, size_t read_size,
+ loff_t *ppos)
{
struct simple_attr *sattr;
uint64_t data;
@@ -119,18 +120,9 @@ simple_attr_read(struct file *filp, char *buf, size_t read_size, loff_t *ppos)
scnprintf(prebuf, sizeof(prebuf), sattr->fmt, data);
- ret = strlen(prebuf) + 1;
- if (*ppos >= ret || read_size < 1) {
- ret = -EINVAL;
- goto unlock;
- }
-
- read_size = min(ret - *ppos, read_size);
- ret = strscpy(buf, prebuf + *ppos, read_size);
-
/* add 1 for null terminator */
- if (ret > 0)
- ret += 1;
+ ret = simple_read_from_buffer(buf, read_size, ppos, prebuf,
+ strlen(prebuf) + 1);
unlock:
mutex_unlock(&sattr->mutex);
@@ -155,29 +147,38 @@ unlock:
* On failure, negative signed ERRNO
*/
static ssize_t
-simple_attr_write_common(struct file *filp, const char *buf, size_t write_size,
- loff_t *ppos, bool is_signed)
+simple_attr_write_common(struct file *filp, const char __user *ubuf,
+ size_t write_size, loff_t *ppos, bool is_signed)
{
struct simple_attr *sattr;
unsigned long long data;
- size_t bufsize;
+ char *buf;
ssize_t ret;
sattr = filp->private_data;
- bufsize = strlen(buf) + 1;
if (sattr->set == NULL)
return (-EFAULT);
- if (*ppos >= bufsize || write_size < 1)
+ if (*ppos != 0 || write_size < 1)
return (-EINVAL);
+ buf = malloc(write_size, M_LSATTR, M_WAITOK);
+ if (copy_from_user(buf, ubuf, write_size) != 0) {
+ free(buf, M_LSATTR);
+ return (-EFAULT);
+ }
+ if (strnlen(buf, write_size) == write_size) {
+ free(buf, M_LSATTR);
+ return (-EINVAL);
+ }
+
mutex_lock(&sattr->mutex);
if (is_signed)
- ret = kstrtoll(buf + *ppos, 0, &data);
+ ret = kstrtoll(buf, 0, &data);
else
- ret = kstrtoull(buf + *ppos, 0, &data);
+ ret = kstrtoull(buf, 0, &data);
if (ret)
goto unlock;
@@ -185,23 +186,24 @@ simple_attr_write_common(struct file *filp, const char *buf, size_t write_size,
if (ret)
goto unlock;
- ret = bufsize - *ppos;
+ ret = write_size;
unlock:
mutex_unlock(&sattr->mutex);
+ free(buf, M_LSATTR);
return (ret);
}
ssize_t
-simple_attr_write(struct file *filp, const char *buf, size_t write_size,
+simple_attr_write(struct file *filp, const char __user *buf, size_t write_size,
loff_t *ppos)
{
return (simple_attr_write_common(filp, buf, write_size, ppos, false));
}
ssize_t
-simple_attr_write_signed(struct file *filp, const char *buf, size_t write_size,
- loff_t *ppos)
+simple_attr_write_signed(struct file *filp, const char __user *buf,
+ size_t write_size, loff_t *ppos)
{
return (simple_attr_write_common(filp, buf, write_size, ppos, true));
}
diff --git a/sys/compat/linuxkpi/common/src/linux_slab.c b/sys/compat/linuxkpi/common/src/linux_slab.c
index 6f71d17a3770..9fe51f0f4e10 100644
--- a/sys/compat/linuxkpi/common/src/linux_slab.c
+++ b/sys/compat/linuxkpi/common/src/linux_slab.c
@@ -239,7 +239,7 @@ lkpi___kmalloc(size_t size, gfp_t flags)
}
void *
-lkpi_krealloc(void *ptr, size_t size, gfp_t flags)
+lkpi_krealloc(const void *ptr, size_t size, gfp_t flags)
{
void *nptr;
size_t osize;
@@ -250,9 +250,14 @@ lkpi_krealloc(void *ptr, size_t size, gfp_t flags)
if (ptr == NULL)
return (kmalloc(size, flags));
+ if (size == 0) {
+ kfree(ptr);
+ return (ZERO_SIZE_PTR);
+ }
+
osize = ksize(ptr);
if (size <= osize)
- return (ptr);
+ return (__DECONST(void *, ptr));
/*
* We know the new size > original size. realloc(9) does not (and cannot)
@@ -262,7 +267,7 @@ lkpi_krealloc(void *ptr, size_t size, gfp_t flags)
* backing.
*/
if (size <= PAGE_SIZE)
- return (realloc(ptr, size, M_KMALLOC, linux_check_m_flags(flags)));
+ return (realloc(__DECONST(void *, ptr), size, M_KMALLOC, linux_check_m_flags(flags)));
nptr = kmalloc(size, flags);
if (nptr == NULL)
@@ -273,6 +278,44 @@ lkpi_krealloc(void *ptr, size_t size, gfp_t flags)
return (nptr);
}
+void *
+lkpi_kvrealloc(const void *ptr, size_t oldsize, size_t newsize, gfp_t flags)
+{
+ void *newptr;
+
+ /*
+ * We replicate the behaviour of krealloc() instead of calling it
+ * because we don't need to allocate physically contiguous memory.
+ */
+
+ if (newsize == 0) {
+ kfree(ptr);
+ return (ZERO_SIZE_PTR);
+ }
+
+ if (ptr == NULL) {
+ newptr = kvmalloc(newsize, flags);
+ return (newptr);
+ }
+
+ newptr = realloc(
+ __DECONST(void *, ptr), newsize, M_KMALLOC,
+ linux_check_m_flags(flags));
+
+ if (newptr == NULL) {
+ newptr = kvmalloc(newsize, flags);
+ if (newptr == NULL)
+ return (NULL);
+
+ if (ptr != NULL) {
+ memcpy(newptr, ptr, oldsize);
+ kfree(ptr);
+ }
+ }
+
+ return (newptr);
+}
+
struct lkpi_kmalloc_ctx {
size_t size;
gfp_t flags;
diff --git a/sys/compat/linuxkpi/common/src/linux_xarray.c b/sys/compat/linuxkpi/common/src/linux_xarray.c
index 3f07f6d7c59f..8caefbaf7e50 100644
--- a/sys/compat/linuxkpi/common/src/linux_xarray.c
+++ b/sys/compat/linuxkpi/common/src/linux_xarray.c
@@ -389,7 +389,7 @@ __xa_empty(struct xarray *xa)
XA_ASSERT_LOCKED(xa);
- return (!radix_tree_iter_find(&xa->xa_head, &iter, &temp));
+ return (!radix_tree_iter_find(&xa->xa_head, &iter, &temp, 0));
}
bool
@@ -426,7 +426,7 @@ __xa_next(struct xarray *xa, unsigned long *pindex, bool not_first)
return (NULL);
}
- found = radix_tree_iter_find(&xa->xa_head, &iter, &ppslot);
+ found = radix_tree_iter_find(&xa->xa_head, &iter, &ppslot, 0);
if (likely(found)) {
retval = *ppslot;
if (retval == NULL_VALUE)