diff options
Diffstat (limited to 'sys/compat/linuxkpi/common')
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, <xq->txq); + lkpi_80211_mo_wake_tx_queue(hw, <xq->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(<xq->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) |
