aboutsummaryrefslogtreecommitdiff
path: root/sys/compat/linuxkpi/common
diff options
context:
space:
mode:
Diffstat (limited to 'sys/compat/linuxkpi/common')
-rw-r--r--sys/compat/linuxkpi/common/include/acpi/acpi.h76
-rw-r--r--sys/compat/linuxkpi/common/include/acpi/acpi_bus.h13
-rw-r--r--sys/compat/linuxkpi/common/include/asm/barrier.h3
-rw-r--r--sys/compat/linuxkpi/common/include/linux/acpi.h4
-rw-r--r--sys/compat/linuxkpi/common/include/linux/acpi_amd_wbrf.h97
-rw-r--r--sys/compat/linuxkpi/common/include/linux/aperture.h16
-rw-r--r--sys/compat/linuxkpi/common/include/linux/bitfield.h6
-rw-r--r--sys/compat/linuxkpi/common/include/linux/bitops.h17
-rw-r--r--sys/compat/linuxkpi/common/include/linux/cc_platform.h1
-rw-r--r--sys/compat/linuxkpi/common/include/linux/cec.h8
-rw-r--r--sys/compat/linuxkpi/common/include/linux/cgroup.h34
-rw-r--r--sys/compat/linuxkpi/common/include/linux/cleanup.h46
-rw-r--r--sys/compat/linuxkpi/common/include/linux/compiler.h6
-rw-r--r--sys/compat/linuxkpi/common/include/linux/completion.h3
-rw-r--r--sys/compat/linuxkpi/common/include/linux/container_of.h8
-rw-r--r--sys/compat/linuxkpi/common/include/linux/cpufeature.h43
-rw-r--r--sys/compat/linuxkpi/common/include/linux/debugfs.h6
-rw-r--r--sys/compat/linuxkpi/common/include/linux/devcoredump.h9
-rw-r--r--sys/compat/linuxkpi/common/include/linux/device.h21
-rw-r--r--sys/compat/linuxkpi/common/include/linux/device/driver.h33
-rw-r--r--sys/compat/linuxkpi/common/include/linux/dma-mapping.h64
-rw-r--r--sys/compat/linuxkpi/common/include/linux/efi.h8
-rw-r--r--sys/compat/linuxkpi/common/include/linux/errno.h1
-rw-r--r--sys/compat/linuxkpi/common/include/linux/etherdevice.h14
-rw-r--r--sys/compat/linuxkpi/common/include/linux/file.h2
-rw-r--r--sys/compat/linuxkpi/common/include/linux/fs.h45
-rw-r--r--sys/compat/linuxkpi/common/include/linux/gfp.h23
-rw-r--r--sys/compat/linuxkpi/common/include/linux/gpf.h33
-rw-r--r--sys/compat/linuxkpi/common/include/linux/hdmi.h13
-rw-r--r--sys/compat/linuxkpi/common/include/linux/highmem.h39
-rw-r--r--sys/compat/linuxkpi/common/include/linux/idr.h2
-rw-r--r--sys/compat/linuxkpi/common/include/linux/ieee80211.h526
-rw-r--r--sys/compat/linuxkpi/common/include/linux/if_ether.h10
-rw-r--r--sys/compat/linuxkpi/common/include/linux/interrupt.h2
-rw-r--r--sys/compat/linuxkpi/common/include/linux/io.h16
-rw-r--r--sys/compat/linuxkpi/common/include/linux/jiffies.h54
-rw-r--r--sys/compat/linuxkpi/common/include/linux/kernel.h311
-rw-r--r--sys/compat/linuxkpi/common/include/linux/kernel_stat.h34
-rw-r--r--sys/compat/linuxkpi/common/include/linux/kmod.h2
-rw-r--r--sys/compat/linuxkpi/common/include/linux/kobject.h2
-rw-r--r--sys/compat/linuxkpi/common/include/linux/kref.h3
-rw-r--r--sys/compat/linuxkpi/common/include/linux/kstrtox.h324
-rw-r--r--sys/compat/linuxkpi/common/include/linux/ktime.h7
-rw-r--r--sys/compat/linuxkpi/common/include/linux/leds.h3
-rw-r--r--sys/compat/linuxkpi/common/include/linux/list.h5
-rw-r--r--sys/compat/linuxkpi/common/include/linux/lockdep.h33
-rw-r--r--sys/compat/linuxkpi/common/include/linux/log2.h96
-rw-r--r--sys/compat/linuxkpi/common/include/linux/math.h76
-rw-r--r--sys/compat/linuxkpi/common/include/linux/math64.h2
-rw-r--r--sys/compat/linuxkpi/common/include/linux/mhi.h2
-rw-r--r--sys/compat/linuxkpi/common/include/linux/minmax.h74
-rw-r--r--sys/compat/linuxkpi/common/include/linux/mm.h114
-rw-r--r--sys/compat/linuxkpi/common/include/linux/mm_types.h11
-rw-r--r--sys/compat/linuxkpi/common/include/linux/mmzone.h4
-rw-r--r--sys/compat/linuxkpi/common/include/linux/module.h21
-rw-r--r--sys/compat/linuxkpi/common/include/linux/mutex.h1
-rw-r--r--sys/compat/linuxkpi/common/include/linux/netdev_features.h25
-rw-r--r--sys/compat/linuxkpi/common/include/linux/netdevice.h11
-rw-r--r--sys/compat/linuxkpi/common/include/linux/nl80211.h33
-rw-r--r--sys/compat/linuxkpi/common/include/linux/nodemask.h46
-rw-r--r--sys/compat/linuxkpi/common/include/linux/page-flags.h7
-rw-r--r--sys/compat/linuxkpi/common/include/linux/page.h1
-rw-r--r--sys/compat/linuxkpi/common/include/linux/pagemap.h11
-rw-r--r--sys/compat/linuxkpi/common/include/linux/pagevec.h68
-rw-r--r--sys/compat/linuxkpi/common/include/linux/pci.h104
-rw-r--r--sys/compat/linuxkpi/common/include/linux/pci_ids.h1
-rw-r--r--sys/compat/linuxkpi/common/include/linux/perf_event.h34
-rw-r--r--sys/compat/linuxkpi/common/include/linux/pm.h30
-rw-r--r--sys/compat/linuxkpi/common/include/linux/pm_runtime.h5
-rw-r--r--sys/compat/linuxkpi/common/include/linux/printk.h61
-rw-r--r--sys/compat/linuxkpi/common/include/linux/pwm.h6
-rw-r--r--sys/compat/linuxkpi/common/include/linux/radix-tree.h1
-rw-r--r--sys/compat/linuxkpi/common/include/linux/random.h9
-rw-r--r--sys/compat/linuxkpi/common/include/linux/rbtree.h24
-rw-r--r--sys/compat/linuxkpi/common/include/linux/rcupdate.h57
-rw-r--r--sys/compat/linuxkpi/common/include/linux/ref_tracker.h93
-rw-r--r--sys/compat/linuxkpi/common/include/linux/refcount.h23
-rw-r--r--sys/compat/linuxkpi/common/include/linux/rwlock.h21
-rw-r--r--sys/compat/linuxkpi/common/include/linux/scatterlist.h10
-rw-r--r--sys/compat/linuxkpi/common/include/linux/sched.h5
-rw-r--r--sys/compat/linuxkpi/common/include/linux/seq_file.h14
-rw-r--r--sys/compat/linuxkpi/common/include/linux/seqlock.h2
-rw-r--r--sys/compat/linuxkpi/common/include/linux/shmem_fs.h10
-rw-r--r--sys/compat/linuxkpi/common/include/linux/shrinker.h17
-rw-r--r--sys/compat/linuxkpi/common/include/linux/skbuff.h348
-rw-r--r--sys/compat/linuxkpi/common/include/linux/slab.h157
-rw-r--r--sys/compat/linuxkpi/common/include/linux/soc/mediatek/mtk_wed.h2
-rw-r--r--sys/compat/linuxkpi/common/include/linux/spinlock.h42
-rw-r--r--sys/compat/linuxkpi/common/include/linux/stdarg.h2
-rw-r--r--sys/compat/linuxkpi/common/include/linux/stddef.h26
-rw-r--r--sys/compat/linuxkpi/common/include/linux/string.h30
-rw-r--r--sys/compat/linuxkpi/common/include/linux/swap.h14
-rw-r--r--sys/compat/linuxkpi/common/include/linux/sysfs.h134
-rw-r--r--sys/compat/linuxkpi/common/include/linux/time.h2
-rw-r--r--sys/compat/linuxkpi/common/include/linux/timer.h6
-rw-r--r--sys/compat/linuxkpi/common/include/linux/types.h1
-rw-r--r--sys/compat/linuxkpi/common/include/linux/units.h40
-rw-r--r--sys/compat/linuxkpi/common/include/linux/wait.h30
-rw-r--r--sys/compat/linuxkpi/common/include/linux/workqueue.h10
-rw-r--r--sys/compat/linuxkpi/common/include/linux/xarray.h13
-rw-r--r--sys/compat/linuxkpi/common/include/net/cfg80211.h689
-rw-r--r--sys/compat/linuxkpi/common/include/net/mac80211.h1126
-rw-r--r--sys/compat/linuxkpi/common/include/net/page_pool.h2
-rw-r--r--sys/compat/linuxkpi/common/include/stdarg.h2
-rw-r--r--sys/compat/linuxkpi/common/include/video/cmdline.h44
-rw-r--r--sys/compat/linuxkpi/common/include/video/vga.h3
-rw-r--r--sys/compat/linuxkpi/common/src/linux_80211.c3211
-rw-r--r--sys/compat/linuxkpi/common/src/linux_80211.h80
-rw-r--r--sys/compat/linuxkpi/common/src/linux_80211_macops.c59
-rw-r--r--sys/compat/linuxkpi/common/src/linux_acpi.c51
-rw-r--r--sys/compat/linuxkpi/common/src/linux_aperture.c86
-rw-r--r--sys/compat/linuxkpi/common/src/linux_cmdline.c63
-rw-r--r--sys/compat/linuxkpi/common/src/linux_compat.c262
-rw-r--r--sys/compat/linuxkpi/common/src/linux_firmware.c22
-rw-r--r--sys/compat/linuxkpi/common/src/linux_folio.c58
-rw-r--r--sys/compat/linuxkpi/common/src/linux_hdmi.c86
-rw-r--r--sys/compat/linuxkpi/common/src/linux_i2c.c16
-rw-r--r--sys/compat/linuxkpi/common/src/linux_i2cbb.c16
-rw-r--r--sys/compat/linuxkpi/common/src/linux_idr.c14
-rw-r--r--sys/compat/linuxkpi/common/src/linux_kobject.c4
-rw-r--r--sys/compat/linuxkpi/common/src/linux_netdev.c19
-rw-r--r--sys/compat/linuxkpi/common/src/linux_page.c79
-rw-r--r--sys/compat/linuxkpi/common/src/linux_pci.c151
-rw-r--r--sys/compat/linuxkpi/common/src/linux_rcu.c32
-rw-r--r--sys/compat/linuxkpi/common/src/linux_schedule.c146
-rw-r--r--sys/compat/linuxkpi/common/src/linux_shrinker.c30
-rw-r--r--sys/compat/linuxkpi/common/src/linux_simple_attr.c27
-rw-r--r--sys/compat/linuxkpi/common/src/linux_skbuff.c102
-rw-r--r--sys/compat/linuxkpi/common/src/linux_slab.c81
-rw-r--r--sys/compat/linuxkpi/common/src/linux_work.c9
-rw-r--r--sys/compat/linuxkpi/common/src/linux_xarray.c61
131 files changed, 7926 insertions, 2718 deletions
diff --git a/sys/compat/linuxkpi/common/include/acpi/acpi.h b/sys/compat/linuxkpi/common/include/acpi/acpi.h
index e0218bdde12e..1e398d05ba20 100644
--- a/sys/compat/linuxkpi/common/include/acpi/acpi.h
+++ b/sys/compat/linuxkpi/common/include/acpi/acpi.h
@@ -3,6 +3,10 @@
*
* Copyright (c) 2017 Mark Johnston <markj@FreeBSD.org>
* Copyright (c) 2020 Vladimir Kondratyev <wulf@FreeBSD.org>
+ * Copyright (c) 2025 The FreeBSD Foundation
+ *
+ * Portions of this software were developed by Björn Zeeb
+ * 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
@@ -31,6 +35,13 @@
#define _LINUXKPI_ACPI_ACPI_H_
/*
+ * LINUXKPI_WANT_LINUX_ACPI is a temporary workaround to allow drm-kmod
+ * to update all needed branches without breaking builds.
+ * Once that happened and checks are implemented based on __FreeBSD_verison
+ * we will remove these conditions again.
+ */
+
+/*
* FreeBSD import of ACPICA has a typedef for BOOLEAN which conflicts with
* amdgpu driver. Workaround it on preprocessor level.
*/
@@ -46,8 +57,8 @@ typedef int64_t INT64;
#include <contrib/dev/acpica/include/acpi.h>
#undef BOOLEAN
+typedef ACPI_IO_ADDRESS acpi_io_address;
typedef ACPI_HANDLE acpi_handle;
-typedef ACPI_OBJECT acpi_object;
typedef ACPI_OBJECT_HANDLER acpi_object_handler;
typedef ACPI_OBJECT_TYPE acpi_object_type;
typedef ACPI_STATUS acpi_status;
@@ -55,12 +66,62 @@ typedef ACPI_STRING acpi_string;
typedef ACPI_SIZE acpi_size;
typedef ACPI_WALK_CALLBACK acpi_walk_callback;
+union linuxkpi_acpi_object {
+ acpi_object_type type;
+ struct {
+ acpi_object_type type;
+ UINT64 value;
+ } integer;
+ struct {
+ acpi_object_type type;
+ UINT32 length;
+ char *pointer;
+ } string;
+ struct {
+ acpi_object_type type;
+ UINT32 length;
+ UINT8 *pointer;
+ } buffer;
+ struct {
+ acpi_object_type type;
+ UINT32 count;
+ union linuxkpi_acpi_object *elements;
+ } package;
+ struct {
+ acpi_object_type type;
+ acpi_object_type actual_type;
+ acpi_handle handle;
+ } reference;
+ struct {
+ acpi_object_type type;
+ UINT32 proc_id;
+ acpi_io_address pblk_address;
+ UINT32 pblk_length;
+ } processor;
+ struct {
+ acpi_object_type type;
+ UINT32 system_level;
+ UINT32 resource_order;
+ } power_resource;
+};
+
+#ifdef LINUXKPI_WANT_LINUX_ACPI
+struct linuxkpi_acpi_buffer {
+ acpi_size length; /* Length in bytes of the buffer */
+ void *pointer; /* pointer to buffer */
+};
+
+typedef struct linuxkpi_acpi_buffer lkpi_acpi_buffer_t;
+#else
+typedef ACPI_BUFFER lkpi_acpi_buffer_t;
+#endif
+
static inline ACPI_STATUS
acpi_evaluate_object(ACPI_HANDLE Object, ACPI_STRING Pathname,
- ACPI_OBJECT_LIST *ParameterObjects, ACPI_BUFFER *ReturnObjectBuffer)
+ ACPI_OBJECT_LIST *ParameterObjects, lkpi_acpi_buffer_t *ReturnObjectBuffer)
{
return (AcpiEvaluateObject(
- Object, Pathname, ParameterObjects, ReturnObjectBuffer));
+ Object, Pathname, ParameterObjects, (ACPI_BUFFER *)ReturnObjectBuffer));
}
static inline const char *
@@ -83,9 +144,9 @@ acpi_get_data(ACPI_HANDLE ObjHandle, ACPI_OBJECT_HANDLER Handler, void **Data)
}
static inline ACPI_STATUS
-acpi_get_name(ACPI_HANDLE Object, UINT32 NameType, ACPI_BUFFER *RetPathPtr)
+acpi_get_name(ACPI_HANDLE Object, UINT32 NameType, lkpi_acpi_buffer_t *RetPathPtr)
{
- return (AcpiGetName(Object, NameType, RetPathPtr));
+ return (AcpiGetName(Object, NameType, (ACPI_BUFFER *)RetPathPtr));
}
static inline ACPI_STATUS
@@ -101,4 +162,9 @@ acpi_put_table(ACPI_TABLE_HEADER *Table)
AcpiPutTable(Table);
}
+#ifdef LINUXKPI_WANT_LINUX_ACPI
+#define acpi_object linuxkpi_acpi_object
+#define acpi_buffer linuxkpi_acpi_buffer
+#endif
+
#endif /* _LINUXKPI_ACPI_ACPI_H_ */
diff --git a/sys/compat/linuxkpi/common/include/acpi/acpi_bus.h b/sys/compat/linuxkpi/common/include/acpi/acpi_bus.h
index f107902a26ad..47195e7d66a6 100644
--- a/sys/compat/linuxkpi/common/include/acpi/acpi_bus.h
+++ b/sys/compat/linuxkpi/common/include/acpi/acpi_bus.h
@@ -29,6 +29,9 @@
#ifndef _LINUXKPI_ACPI_ACPI_BUS_H_
#define _LINUXKPI_ACPI_ACPI_BUS_H_
+/* Aliase struct acpi_device to device_t */
+#define acpi_device _device
+
typedef char acpi_device_class[20];
struct acpi_bus_event {
@@ -38,6 +41,8 @@ struct acpi_bus_event {
};
#define acpi_dev_present(...) lkpi_acpi_dev_present(__VA_ARGS__)
+#define acpi_dev_get_first_match_dev(...) \
+ lkpi_acpi_dev_get_first_match_dev(__VA_ARGS__)
ACPI_HANDLE bsd_acpi_get_handle(device_t bsddev);
bool acpi_check_dsm(ACPI_HANDLE handle, const char *uuid, int rev,
@@ -50,5 +55,13 @@ int unregister_acpi_notifier(struct notifier_block *nb);
uint32_t acpi_target_system_state(void);
bool lkpi_acpi_dev_present(const char *hid, const char *uid,
int64_t hrv);
+struct acpi_device *lkpi_acpi_dev_get_first_match_dev(const char *hid,
+ const char *uid, int64_t hrv);
+
+union linuxkpi_acpi_object;
+
+union linuxkpi_acpi_object *
+acpi_evaluate_dsm(ACPI_HANDLE ObjHandle, const guid_t *guid,
+ UINT64 rev, UINT64 func, union linuxkpi_acpi_object *arg);
#endif /* _LINUXKPI_ACPI_ACPI_BUS_H_ */
diff --git a/sys/compat/linuxkpi/common/include/asm/barrier.h b/sys/compat/linuxkpi/common/include/asm/barrier.h
index d09f9c6d2c97..39c5139cb322 100644
--- a/sys/compat/linuxkpi/common/include/asm/barrier.h
+++ b/sys/compat/linuxkpi/common/include/asm/barrier.h
@@ -58,4 +58,7 @@
#define smp_mb__before_atomic() barrier()
#define smp_mb__after_atomic() barrier()
+#define smp_store_release(p, v) do { smp_mb(); WRITE_ONCE(*p, v); } while (0)
+#define smp_load_acquire(p) ({ typeof(*p) _v = READ_ONCE(*p); smp_mb(); _v; })
+
#endif /* _LINUXKPI_ASM_BARRIER_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/acpi.h b/sys/compat/linuxkpi/common/include/linux/acpi.h
index 610aa0784cb9..3e1ec1b20626 100644
--- a/sys/compat/linuxkpi/common/include/linux/acpi.h
+++ b/sys/compat/linuxkpi/common/include/linux/acpi.h
@@ -39,6 +39,10 @@
#define ACPI_HANDLE(dev) \
((dev)->bsddev != NULL ? bsd_acpi_get_handle((dev)->bsddev) : NULL)
+#define acpi_device_handle(dev) \
+ ((dev) != NULL ? bsd_acpi_get_handle(dev) : NULL)
+static inline void acpi_dev_put(struct acpi_device *adev) {}
+#define acpi_handle_debug(handle, fmt, ...)
#endif
diff --git a/sys/compat/linuxkpi/common/include/linux/acpi_amd_wbrf.h b/sys/compat/linuxkpi/common/include/linux/acpi_amd_wbrf.h
new file mode 100644
index 000000000000..92c2ead41c45
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/acpi_amd_wbrf.h
@@ -0,0 +1,97 @@
+/*-
+ * 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_ACPI_AMD_WBRF_H_
+#define _LINUXKPI_LINUX_ACPI_AMD_WBRF_H_
+
+#include <linux/device.h>
+#include <linux/notifier.h>
+
+#define MAX_NUM_OF_WBRF_RANGES 11
+
+#define WBRF_RECORD_ADD 0x0
+#define WBRF_RECORD_REMOVE 0x1
+
+struct freq_band_range {
+ uint64_t start;
+ uint64_t end;
+};
+
+struct wbrf_ranges_in_out {
+ uint64_t num_of_ranges;
+ struct freq_band_range band_list[MAX_NUM_OF_WBRF_RANGES];
+};
+
+enum wbrf_notifier_actions {
+ WBRF_CHANGED,
+};
+
+/*
+ * The following functions currently have dummy implementations that, on Linux,
+ * are used when CONFIG_AMD_WBRF is not set at compile time.
+ */
+
+static inline bool
+acpi_amd_wbrf_supported_consumer(struct device *dev)
+{
+ return (false);
+}
+
+static inline int
+acpi_amd_wbrf_add_remove(struct device *dev, uint8_t action,
+ struct wbrf_ranges_in_out *in)
+{
+ return (-ENODEV);
+}
+
+static inline bool
+acpi_amd_wbrf_supported_producer(struct device *dev)
+{
+ return (false);
+}
+
+static inline int
+amd_wbrf_retrieve_freq_band(struct device *dev, struct wbrf_ranges_in_out *out)
+{
+ return (-ENODEV);
+}
+
+static inline int
+amd_wbrf_register_notifier(struct notifier_block *nb)
+{
+ return (-ENODEV);
+}
+
+static inline int
+amd_wbrf_unregister_notifier(struct notifier_block *nb)
+{
+ return (-ENODEV);
+}
+
+#endif /* _LINUXKPI_LINUX_ACPI_AMD_WBRF_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/aperture.h b/sys/compat/linuxkpi/common/include/linux/aperture.h
index e0387ed0225d..7eced3cc3cb1 100644
--- a/sys/compat/linuxkpi/common/include/linux/aperture.h
+++ b/sys/compat/linuxkpi/common/include/linux/aperture.h
@@ -16,7 +16,9 @@ int devm_aperture_acquire_for_platform_device(struct platform_device *pdev,
resource_size_t size);
int aperture_remove_conflicting_devices(resource_size_t base, resource_size_t size,
- bool primary, const char *name);
+ const char *name);
+
+int __aperture_remove_legacy_vga_devices(struct pci_dev *pdev);
int aperture_remove_conflicting_pci_devices(struct pci_dev *pdev, const char *name);
#else
@@ -28,7 +30,12 @@ static inline int devm_aperture_acquire_for_platform_device(struct platform_devi
}
static inline int aperture_remove_conflicting_devices(resource_size_t base, resource_size_t size,
- bool primary, const char *name)
+ const char *name)
+{
+ return 0;
+}
+
+static inline int __aperture_remove_legacy_vga_devices(struct pci_dev *pdev)
{
return 0;
}
@@ -41,7 +48,6 @@ static inline int aperture_remove_conflicting_pci_devices(struct pci_dev *pdev,
/**
* aperture_remove_all_conflicting_devices - remove all existing framebuffers
- * @primary: also kick vga16fb if present; only relevant for VGA devices
* @name: a descriptive name of the requesting driver
*
* This function removes all graphics device drivers. Use this function on systems
@@ -50,9 +56,9 @@ static inline int aperture_remove_conflicting_pci_devices(struct pci_dev *pdev,
* Returns:
* 0 on success, or a negative errno code otherwise
*/
-static inline int aperture_remove_all_conflicting_devices(bool primary, const char *name)
+static inline int aperture_remove_all_conflicting_devices(const char *name)
{
- return aperture_remove_conflicting_devices(0, (resource_size_t)-1, primary, name);
+ return aperture_remove_conflicting_devices(0, (resource_size_t)-1, name);
}
#endif
diff --git a/sys/compat/linuxkpi/common/include/linux/bitfield.h b/sys/compat/linuxkpi/common/include/linux/bitfield.h
index a2020d247489..8a91b0663f37 100644
--- a/sys/compat/linuxkpi/common/include/linux/bitfield.h
+++ b/sys/compat/linuxkpi/common/include/linux/bitfield.h
@@ -1,7 +1,7 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2020 The FreeBSD Foundation
+ * Copyright (c) 2020-2024 The FreeBSD Foundation
*
* This software was developed by Björn Zeeb under sponsorship from
* the FreeBSD Foundation.
@@ -131,6 +131,10 @@ _uX_replace_bits(8)
#define FIELD_PREP(_mask, _value) \
(((typeof(_mask))(_value) << __bf_shf(_mask)) & (_mask))
+/* Likely would need extra sanity checks compared to FIELD_PREP()? */
+#define FIELD_PREP_CONST(_mask, _value) \
+ (((typeof(_mask))(_value) << __bf_shf(_mask)) & (_mask))
+
#define FIELD_GET(_mask, _value) \
((typeof(_mask))(((_value) & (_mask)) >> __bf_shf(_mask)))
diff --git a/sys/compat/linuxkpi/common/include/linux/bitops.h b/sys/compat/linuxkpi/common/include/linux/bitops.h
index 1415d5224084..bc776a0db9c4 100644
--- a/sys/compat/linuxkpi/common/include/linux/bitops.h
+++ b/sys/compat/linuxkpi/common/include/linux/bitops.h
@@ -62,10 +62,10 @@
#define hweight64(x) bitcount64(x)
#define hweight_long(x) bitcountl(x)
-#define HWEIGHT8(x) (bitcount8((uint8_t)(x)) + 1)
-#define HWEIGHT16(x) (bitcount16(x) + 1)
-#define HWEIGHT32(x) (bitcount32(x) + 1)
-#define HWEIGHT64(x) (bitcount64(x) + 1)
+#define HWEIGHT8(x) (bitcount8((uint8_t)(x)))
+#define HWEIGHT16(x) (bitcount16(x))
+#define HWEIGHT32(x) (bitcount32(x))
+#define HWEIGHT64(x) (bitcount64(x))
static inline int
__ffs(int mask)
@@ -288,6 +288,15 @@ find_next_zero_bit(const unsigned long *addr, unsigned long size,
#define test_bit(i, a) \
!!(READ_ONCE(((volatile const unsigned long *)(a))[BIT_WORD(i)]) & BIT_MASK(i))
+static inline void
+__assign_bit(long bit, volatile unsigned long *addr, bool value)
+{
+ if (value)
+ __set_bit(bit, addr);
+ else
+ __clear_bit(bit, addr);
+}
+
static inline int
test_and_clear_bit(long bit, volatile unsigned long *var)
{
diff --git a/sys/compat/linuxkpi/common/include/linux/cc_platform.h b/sys/compat/linuxkpi/common/include/linux/cc_platform.h
index 01c0de17f7d2..1544c141614b 100644
--- a/sys/compat/linuxkpi/common/include/linux/cc_platform.h
+++ b/sys/compat/linuxkpi/common/include/linux/cc_platform.h
@@ -8,6 +8,7 @@
enum cc_attr {
CC_ATTR_MEM_ENCRYPT,
+ CC_ATTR_GUEST_MEM_ENCRYPT,
};
static inline bool
diff --git a/sys/compat/linuxkpi/common/include/linux/cec.h b/sys/compat/linuxkpi/common/include/linux/cec.h
new file mode 100644
index 000000000000..e0854d87d85c
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/cec.h
@@ -0,0 +1,8 @@
+/* Public domain */
+
+#ifndef _LINUXKPI_LINUX_CEC_H_
+#define _LINUXKPI_LINUX_CEC_H_
+
+#define CEC_PHYS_ADDR_INVALID 0xffff
+
+#endif /* _LINUXKPI_LINUX_CEC_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/cgroup.h b/sys/compat/linuxkpi/common/include/linux/cgroup.h
new file mode 100644
index 000000000000..a9dd22fd0f4c
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/cgroup.h
@@ -0,0 +1,34 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 Jean-Sébastien Pédron <dumbbell@FreeBSD.org>
+ *
+ * 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_CGROUP_H_
+#define _LINUXKPI_LINUX_CGROUP_H_
+
+#include <linux/kernel_stat.h>
+
+#endif /* _LINUXKPI_LINUX_CGROUP_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/cleanup.h b/sys/compat/linuxkpi/common/include/linux/cleanup.h
new file mode 100644
index 000000000000..01f234f0cbe7
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/cleanup.h
@@ -0,0 +1,46 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 The FreeBSD Foundation
+ *
+ * This software was developed by Björn Zeeb under sponsorship from
+ * the FreeBSD Foundation.
+ */
+
+#ifndef _LINUXKPI_LINUX_CLEANUP_H
+#define _LINUXKPI_LINUX_CLEANUP_H
+
+#define __cleanup(_f) __attribute__((__cleanup__(_f)))
+
+/*
+ * 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; \
+ \
+ static inline _dt \
+ guard_ ## _n ## _create( _dt _T) \
+ { \
+ _dt c; \
+ \
+ c = ({ _lock; _T; }); \
+ return (c); \
+ } \
+ \
+ static inline void \
+ guard_ ## _n ## _destroy(_dt *t) \
+ { \
+ _dt _T; \
+ \
+ _T = *t; \
+ if (_T) { _unlock; }; \
+ }
+
+/* We need to keep these calls unique. */
+#define guard(_n) \
+ guard_ ## _n ## _t guard_ ## _n ## _ ## __COUNTER__ \
+ __cleanup(guard_ ## _n ## _destroy) = guard_ ## _n ## _create
+
+#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 d59e6faed12d..fb5ad3bf4fe4 100644
--- a/sys/compat/linuxkpi/common/include/linux/compiler.h
+++ b/sys/compat/linuxkpi/common/include/linux/compiler.h
@@ -48,7 +48,9 @@
#define __cond_lock(x,c) (c)
#define __bitwise
#define __devinitdata
+#ifndef __deprecated
#define __deprecated
+#endif
#define __init
#define __initconst
#define __devinit
@@ -89,6 +91,10 @@
#define __printf(a,b) __printflike(a,b)
+#define __diag_push()
+#define __diag_pop()
+#define __diag_ignore_all(...)
+
#define barrier() __asm__ __volatile__("": : :"memory")
#define lower_32_bits(n) ((u32)(n))
diff --git a/sys/compat/linuxkpi/common/include/linux/completion.h b/sys/compat/linuxkpi/common/include/linux/completion.h
index 26e41a51c10b..9f8bebb4cf82 100644
--- a/sys/compat/linuxkpi/common/include/linux/completion.h
+++ b/sys/compat/linuxkpi/common/include/linux/completion.h
@@ -60,7 +60,8 @@ struct completion {
extern void linux_complete_common(struct completion *, int);
extern int linux_wait_for_common(struct completion *, int);
-extern int linux_wait_for_timeout_common(struct completion *, int, int);
+extern unsigned long linux_wait_for_timeout_common(struct completion *,
+ unsigned long, int);
extern int linux_try_wait_for_completion(struct completion *);
extern int linux_completion_done(struct completion *);
diff --git a/sys/compat/linuxkpi/common/include/linux/container_of.h b/sys/compat/linuxkpi/common/include/linux/container_of.h
index 449507fcf9c1..7210d531b055 100644
--- a/sys/compat/linuxkpi/common/include/linux/container_of.h
+++ b/sys/compat/linuxkpi/common/include/linux/container_of.h
@@ -41,6 +41,14 @@
(type *)((uintptr_t)__p - offsetof(type, member)); \
})
+#define container_of_const(ptr, type, member) \
+ _Generic(ptr, \
+ const typeof(*(ptr)) *: \
+ (const type *)container_of(ptr, type, member), \
+ default: \
+ container_of(ptr, type, member) \
+ )
+
#define typeof_member(type, member) __typeof(((type *)0)->member)
#endif
diff --git a/sys/compat/linuxkpi/common/include/linux/cpufeature.h b/sys/compat/linuxkpi/common/include/linux/cpufeature.h
new file mode 100644
index 000000000000..746d1a7164a8
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/cpufeature.h
@@ -0,0 +1,43 @@
+/*-
+ * 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_CPUFEATURE_H_
+#define _LINUXKPI_LINUX_CPUFEATURE_H_
+
+/*
+ * Linux includes the following header. We don't have it on FreeBSD yet, so
+ * let's comment this include for now. It is still referenced here because
+ * sometimes, consumers of headers rely voluntarily or not on the namespace
+ * pollution.
+ */
+/* #include <linux/init.h> */
+#include <linux/mod_devicetable.h>
+#include <asm/cpufeature.h>
+
+#endif /* _LINUXKPI_LINUX_CPUFEATURE_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/debugfs.h b/sys/compat/linuxkpi/common/include/linux/debugfs.h
index 470b50cb730c..4d146e085a7b 100644
--- a/sys/compat/linuxkpi/common/include/linux/debugfs.h
+++ b/sys/compat/linuxkpi/common/include/linux/debugfs.h
@@ -82,12 +82,16 @@ struct dentry *debugfs_create_dir(const char *name, struct dentry *parent);
struct dentry *debugfs_create_symlink(const char *name, struct dentry *parent,
const char *dest);
+struct dentry *debugfs_lookup(const char *name, struct dentry *parent);
+
void debugfs_remove(struct dentry *dentry);
void debugfs_remove_recursive(struct dentry *dentry);
#define DEFINE_DEBUGFS_ATTRIBUTE(__fops, __get, __set, __fmt) \
DEFINE_SIMPLE_ATTRIBUTE(__fops, __get, __set, __fmt)
+#define DEFINE_DEBUGFS_ATTRIBUTE_SIGNED(__fops, __get, __set, __fmt) \
+ DEFINE_SIMPLE_ATTRIBUTE_SIGNED(__fops, __get, __set, __fmt)
void debugfs_create_bool(const char *name, umode_t mode, struct dentry *parent,
bool *value);
@@ -111,6 +115,8 @@ void debugfs_create_ulong(const char *name, umode_t mode, struct dentry *parent,
unsigned long *value);
void debugfs_create_atomic_t(const char *name, umode_t mode, struct dentry *parent,
atomic_t *value);
+void debugfs_create_str(const char *name, umode_t mode, struct dentry *parent,
+ char **value);
struct dentry *debugfs_create_blob(const char *name, umode_t mode,
struct dentry *parent, struct debugfs_blob_wrapper *value);
diff --git a/sys/compat/linuxkpi/common/include/linux/devcoredump.h b/sys/compat/linuxkpi/common/include/linux/devcoredump.h
index b58c490615ad..5fa06c6595a8 100644
--- a/sys/compat/linuxkpi/common/include/linux/devcoredump.h
+++ b/sys/compat/linuxkpi/common/include/linux/devcoredump.h
@@ -1,7 +1,7 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2020 The FreeBSD Foundation
+ * Copyright (c) 2020-2025 The FreeBSD Foundation
*
* This software was developed by Björn Zeeb under sponsorship from
* the FreeBSD Foundation.
@@ -71,4 +71,11 @@ dev_coredumpsg(struct device *dev __unused, struct scatterlist *table,
_lkpi_dev_coredumpsg_free(table);
}
+static inline void
+_devcd_free_sgtable(struct scatterlist *table)
+{
+ /* UNIMPLEMENTED */
+ _lkpi_dev_coredumpsg_free(table);
+}
+
#endif /* _LINUXKPI_LINUX_DEVCOREDUMP_H */
diff --git a/sys/compat/linuxkpi/common/include/linux/device.h b/sys/compat/linuxkpi/common/include/linux/device.h
index 668fe9cb8650..2556b0c45e49 100644
--- a/sys/compat/linuxkpi/common/include/linux/device.h
+++ b/sys/compat/linuxkpi/common/include/linux/device.h
@@ -57,7 +57,6 @@ struct device;
struct class {
const char *name;
- struct module *owner;
struct kobject kobj;
devclass_t bsdclass;
const struct dev_pm_ops *pm;
@@ -91,6 +90,8 @@ struct dev_pm_ops {
struct device_driver {
const char *name;
const struct dev_pm_ops *pm;
+
+ void (*shutdown) (struct device *);
};
struct device_type {
@@ -331,6 +332,13 @@ dev_name(const struct device *dev)
return kobject_name(&dev->kobj);
}
+static inline bool
+dev_is_removable(struct device *dev)
+{
+
+ return (false);
+}
+
#define dev_set_name(_dev, _fmt, ...) \
kobject_set_name(&(_dev)->kobj, (_fmt), ##__VA_ARGS__)
@@ -342,7 +350,12 @@ put_device(struct device *dev)
kobject_put(&dev->kobj);
}
-struct class *class_create(struct module *owner, const char *name);
+struct class *lkpi_class_create(const char *name);
+#if defined(LINUXKPI_VERSION) && LINUXKPI_VERSION >= 60400
+#define class_create(name) lkpi_class_create(name)
+#else
+#define class_create(owner, name) lkpi_class_create(name)
+#endif
static inline int
class_register(struct class *class)
@@ -698,4 +711,8 @@ int lkpi_devm_add_action_or_reset(struct device *dev, void (*action)(void *), vo
#define devm_add_action_or_reset(dev, action, data) \
lkpi_devm_add_action_or_reset(dev, action, data)
+int lkpi_devm_device_add_group(struct device *dev, const struct attribute_group *group);
+#define devm_device_add_group(dev, group) \
+ lkpi_devm_device_add_group(dev, group)
+
#endif /* _LINUXKPI_LINUX_DEVICE_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/device/driver.h b/sys/compat/linuxkpi/common/include/linux/device/driver.h
new file mode 100644
index 000000000000..03b510c9c8b7
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/device/driver.h
@@ -0,0 +1,33 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2021 Bjoern A. Zeeb
+ * Copyright (c) 2024 The FreeBSD Foundation
+ *
+ * Portions of this software were developed by Björn Zeeb
+ * under sponsorship from the FreeBSD Foundation.
+ */
+
+#ifndef LINUXKPI_LINUX_DEVICE_DRIVER_H
+#define LINUXKPI_LINUX_DEVICE_DRIVER_H
+
+#include <sys/cdefs.h>
+#include <linux/module.h>
+
+#define module_driver(_drv, _regf, _unregf) \
+static inline int \
+__CONCAT(__CONCAT(_, _drv), _init)(void) \
+{ \
+ return (_regf(&(_drv))); \
+} \
+ \
+static inline void \
+__CONCAT(__CONCAT(_, _drv), _exit)(void) \
+{ \
+ _unregf(&(_drv)); \
+} \
+ \
+module_init(__CONCAT(__CONCAT(_, _drv), _init)); \
+module_exit(__CONCAT(__CONCAT(_, _drv), _exit))
+
+#endif /* LINUXKPI_LINUX_DEVICE_DRIVER_H */
diff --git a/sys/compat/linuxkpi/common/include/linux/dma-mapping.h b/sys/compat/linuxkpi/common/include/linux/dma-mapping.h
index 84f0361de765..2d8e1196d3d3 100644
--- a/sys/compat/linuxkpi/common/include/linux/dma-mapping.h
+++ b/sys/compat/linuxkpi/common/include/linux/dma-mapping.h
@@ -96,13 +96,17 @@ 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);
-dma_addr_t linux_dma_map_phys(struct device *dev, vm_paddr_t phys, size_t len);
-void linux_dma_unmap(struct device *dev, dma_addr_t dma_addr, size_t size);
+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);
+void linux_dma_unmap(struct device *dev, dma_addr_t dma_addr, size_t size); /* backward compat */
+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 dir __unused,
+ int nents, enum dma_data_direction direction,
unsigned long attrs __unused);
void linux_dma_unmap_sg_attrs(struct device *dev, struct scatterlist *sg,
- int nents __unused, enum dma_data_direction dir __unused,
+ int nents __unused, enum dma_data_direction direction,
unsigned long attrs __unused);
void linuxkpi_dma_sync(struct device *, dma_addr_t, size_t, bus_dmasync_op_t);
@@ -173,16 +177,17 @@ dma_free_coherent(struct device *dev, size_t size, void *cpu_addr,
dma_addr_t dma_addr)
{
- linux_dma_unmap(dev, dma_addr, size);
+ lkpi_dma_unmap(dev, dma_addr, size, DMA_BIDIRECTIONAL, 0);
kmem_free(cpu_addr, size);
}
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 dir, unsigned long attrs)
+ size_t size, enum dma_data_direction direction, unsigned long attrs)
{
- return (linux_dma_map_phys(dev, page_to_phys(page) + offset, size));
+ return (lkpi_dma_map_phys(dev, page_to_phys(page) + offset, size,
+ direction, attrs));
}
/* linux_dma_(un)map_sg_attrs does not support attrs yet */
@@ -197,7 +202,8 @@ dma_map_page(struct device *dev, struct page *page,
unsigned long offset, size_t size, enum dma_data_direction direction)
{
- return (linux_dma_map_phys(dev, page_to_phys(page) + offset, size));
+ return (lkpi_dma_map_phys(dev, page_to_phys(page) + offset, size,
+ direction, 0));
}
static inline void
@@ -205,7 +211,21 @@ dma_unmap_page(struct device *dev, dma_addr_t dma_address, size_t size,
enum dma_data_direction direction)
{
- linux_dma_unmap(dev, dma_address, size);
+ lkpi_dma_unmap(dev, dma_address, size, direction, 0);
+}
+
+static inline dma_addr_t
+dma_map_resource(struct device *dev, phys_addr_t paddr, size_t size,
+ enum dma_data_direction direction, unsigned long attrs)
+{
+ return (lkpi_dma_map_phys(dev, paddr, size, direction, attrs));
+}
+
+static inline void
+dma_unmap_resource(struct device *dev, dma_addr_t dma, size_t size,
+ enum dma_data_direction direction, unsigned long attrs)
+{
+ lkpi_dma_unmap(dev, dma, size, direction, attrs);
}
static inline void
@@ -263,28 +283,33 @@ dma_sync_single_for_device(struct device *dev, dma_addr_t dma,
linuxkpi_dma_sync(dev, dma, size, op);
}
+/* (20250329) These four seem to be unused code. */
static inline void
dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems,
enum dma_data_direction direction)
{
+ pr_debug("%s:%d: TODO dir %d\n", __func__, __LINE__, direction);
}
static inline void
dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems,
enum dma_data_direction direction)
{
+ pr_debug("%s:%d: TODO dir %d\n", __func__, __LINE__, direction);
}
static inline void
dma_sync_single_range_for_cpu(struct device *dev, dma_addr_t dma_handle,
- unsigned long offset, size_t size, int direction)
+ unsigned long offset, size_t size, enum dma_data_direction direction)
{
+ pr_debug("%s:%d: TODO dir %d\n", __func__, __LINE__, direction);
}
static inline void
dma_sync_single_range_for_device(struct device *dev, dma_addr_t dma_handle,
- unsigned long offset, size_t size, int direction)
+ unsigned long offset, size_t size, enum dma_data_direction direction)
{
+ pr_debug("%s:%d: TODO dir %d\n", __func__, __LINE__, direction);
}
#define DMA_MAPPING_ERROR (~(dma_addr_t)0)
@@ -306,24 +331,17 @@ static inline unsigned int dma_set_max_seg_size(struct device *dev,
static inline dma_addr_t
_dma_map_single_attrs(struct device *dev, void *ptr, size_t size,
- enum dma_data_direction direction, unsigned long attrs __unused)
+ enum dma_data_direction direction, unsigned long attrs)
{
- dma_addr_t dma;
-
- dma = linux_dma_map_phys(dev, vtophys(ptr), size);
- if (!dma_mapping_error(dev, dma))
- dma_sync_single_for_device(dev, dma, size, direction);
-
- return (dma);
+ return (lkpi_dma_map_phys(dev, vtophys(ptr), size,
+ direction, attrs));
}
static inline void
_dma_unmap_single_attrs(struct device *dev, dma_addr_t dma, size_t size,
- enum dma_data_direction direction, unsigned long attrs __unused)
+ enum dma_data_direction direction, unsigned long attrs)
{
-
- dma_sync_single_for_cpu(dev, dma, size, direction);
- linux_dma_unmap(dev, dma, size);
+ lkpi_dma_unmap(dev, dma, size, direction, attrs);
}
static inline size_t
diff --git a/sys/compat/linuxkpi/common/include/linux/efi.h b/sys/compat/linuxkpi/common/include/linux/efi.h
index a485b4b1fd94..aa33371bd0e8 100644
--- a/sys/compat/linuxkpi/common/include/linux/efi.h
+++ b/sys/compat/linuxkpi/common/include/linux/efi.h
@@ -41,9 +41,6 @@
static inline bool
__efi_enabled(int feature)
{
-#if defined(MODINFOMD_EFI_MAP) && !defined(__amd64__)
- caddr_t kmdp;
-#endif
bool enabled = false;
switch (feature) {
@@ -52,10 +49,7 @@ __efi_enabled(int feature)
/* Use cached value on amd64 */
enabled = efi_boot;
#elif defined(MODINFOMD_EFI_MAP)
- kmdp = preload_search_by_type("elf kernel");
- if (kmdp == NULL)
- kmdp = preload_search_by_type("elf64 kernel");
- enabled = preload_search_info(kmdp,
+ enabled = preload_search_info(preload_kmdp,
MODINFO_METADATA | MODINFOMD_EFI_MAP) != NULL;
#endif
break;
diff --git a/sys/compat/linuxkpi/common/include/linux/errno.h b/sys/compat/linuxkpi/common/include/linux/errno.h
index ea258587c6f7..d634675d43d0 100644
--- a/sys/compat/linuxkpi/common/include/linux/errno.h
+++ b/sys/compat/linuxkpi/common/include/linux/errno.h
@@ -68,5 +68,6 @@
#define ENOMEDIUM 532
#define ENOSR 533
#define ELNRNG 534
+#define ENAVAIL 535
#endif /* _LINUXKPI_LINUX_ERRNO_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/etherdevice.h b/sys/compat/linuxkpi/common/include/linux/etherdevice.h
index 89cd4c8e0ba0..1f2d6cf22d7e 100644
--- a/sys/compat/linuxkpi/common/include/linux/etherdevice.h
+++ b/sys/compat/linuxkpi/common/include/linux/etherdevice.h
@@ -53,19 +53,27 @@ struct ethtool_modinfo {
static inline bool
is_zero_ether_addr(const u8 * addr)
{
- return ((addr[0] + addr[1] + addr[2] + addr[3] + addr[4] + addr[5]) == 0x00);
+ return ((addr[0] | addr[1] | addr[2] | addr[3] | addr[4] | addr[5]) ==
+ 0x00);
+}
+
+static inline bool
+is_unicast_ether_addr(const u8 * addr)
+{
+ return ((addr[0] & 0x01) == 0x00);
}
static inline bool
is_multicast_ether_addr(const u8 * addr)
{
- return (0x01 & addr[0]);
+ return ((addr[0] & 0x01) == 0x01);
}
static inline bool
is_broadcast_ether_addr(const u8 * addr)
{
- return ((addr[0] + addr[1] + addr[2] + addr[3] + addr[4] + addr[5]) == (6 * 0xff));
+ return ((addr[0] & addr[1] & addr[2] & addr[3] & addr[4] & addr[5]) ==
+ 0xff);
}
static inline bool
diff --git a/sys/compat/linuxkpi/common/include/linux/file.h b/sys/compat/linuxkpi/common/include/linux/file.h
index f94e3d89ced1..f6e988c2d88e 100644
--- a/sys/compat/linuxkpi/common/include/linux/file.h
+++ b/sys/compat/linuxkpi/common/include/linux/file.h
@@ -43,7 +43,7 @@ struct linux_file;
#undef file
-extern struct fileops linuxfileops;
+extern const struct fileops linuxfileops;
static inline struct linux_file *
linux_fget(unsigned int fd)
diff --git a/sys/compat/linuxkpi/common/include/linux/fs.h b/sys/compat/linuxkpi/common/include/linux/fs.h
index a28d5bf97121..f1568ad6282d 100644
--- a/sys/compat/linuxkpi/common/include/linux/fs.h
+++ b/sys/compat/linuxkpi/common/include/linux/fs.h
@@ -150,6 +150,11 @@ struct file_operations {
* an illegal seek error
*/
off_t (*llseek)(struct linux_file *, off_t, int);
+/*
+ * Not supported in FreeBSD. That's ok, we never call it and it allows some
+ * drivers like DRM drivers to compile without changes.
+ */
+ void (*show_fdinfo)(struct seq_file *, struct file *);
#if 0
/* We do not support these methods. Don't permit them to compile. */
loff_t (*llseek)(struct file *, loff_t, int);
@@ -264,12 +269,18 @@ get_file(struct linux_file *f)
return (f);
}
+struct linux_file * linux_get_file_rcu(struct linux_file **f);
+struct linux_file * get_file_active(struct linux_file **f);
+#if defined(LINUXKPI_VERSION) && LINUXKPI_VERSION < 60700
static inline bool
get_file_rcu(struct linux_file *f)
{
return (refcount_acquire_if_not_zero(
f->_file == NULL ? &f->f_count : &f->_file->f_count));
}
+#else
+#define get_file_rcu(f) linux_get_file_rcu(f)
+#endif
static inline struct inode *
igrab(struct inode *inode)
@@ -353,9 +364,8 @@ 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 *read_pos = ((char *) orig) + *ppos;
+ void *p, *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;
@@ -363,18 +373,23 @@ simple_read_from_buffer(void __user *dest, size_t read_size, loff_t *ppos,
if (read_size > buf_remain)
read_size = buf_remain;
- /* 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);
+ /*
+ * 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);
}
MALLOC_DECLARE(M_LSATTR);
-#define DEFINE_SIMPLE_ATTRIBUTE(__fops, __get, __set, __fmt) \
+#define __DEFINE_SIMPLE_ATTRIBUTE(__fops, __get, __set, __fmt, __wrfunc)\
static inline int \
__fops ## _open(struct inode *inode, struct file *filp) \
{ \
@@ -385,10 +400,15 @@ static const struct file_operations __fops = { \
.open = __fops ## _open, \
.release = simple_attr_release, \
.read = simple_attr_read, \
- .write = simple_attr_write, \
+ .write = __wrfunc, \
.llseek = no_llseek \
}
+#define DEFINE_SIMPLE_ATTRIBUTE(fops, get, set, fmt) \
+ __DEFINE_SIMPLE_ATTRIBUTE(fops, get, set, fmt, simple_attr_write)
+#define DEFINE_SIMPLE_ATTRIBUTE_SIGNED(fops, get, set, fmt) \
+ __DEFINE_SIMPLE_ATTRIBUTE(fops, get, set, fmt, simple_attr_write_signed)
+
int simple_attr_open(struct inode *inode, struct file *filp,
int (*get)(void *, uint64_t *), int (*set)(void *, uint64_t),
const char *fmt);
@@ -399,4 +419,7 @@ ssize_t simple_attr_read(struct file *filp, char *buf, size_t read_size, loff_t
ssize_t simple_attr_write(struct file *filp, const char *buf, size_t write_size, loff_t *ppos);
+ssize_t simple_attr_write_signed(struct file *filp, const char *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 f6cce379924d..4c4caa621789 100644
--- a/sys/compat/linuxkpi/common/include/linux/gfp.h
+++ b/sys/compat/linuxkpi/common/include/linux/gfp.h
@@ -43,7 +43,6 @@
#define __GFP_NOWARN 0
#define __GFP_HIGHMEM 0
#define __GFP_ZERO M_ZERO
-#define __GFP_NORETRY 0
#define __GFP_NOMEMALLOC 0
#define __GFP_RECLAIM 0
#define __GFP_RECLAIMABLE 0
@@ -57,7 +56,8 @@
#define __GFP_KSWAPD_RECLAIM 0
#define __GFP_WAIT M_WAITOK
#define __GFP_DMA32 (1U << 24) /* LinuxKPI only */
-#define __GFP_BITS_SHIFT 25
+#define __GFP_NORETRY (1U << 25) /* LinuxKPI only */
+#define __GFP_BITS_SHIFT 26
#define __GFP_BITS_MASK ((1 << __GFP_BITS_SHIFT) - 1)
#define __GFP_NOFAIL M_WAITOK
@@ -85,19 +85,10 @@ struct page_frag_cache {
};
/*
- * Resolve a page into a virtual address:
- *
- * NOTE: This function only works for pages allocated by the kernel.
- */
-extern void *linux_page_address(struct page *);
-
-#define page_address(page) linux_page_address(page)
-
-/*
* Page management for unmapped pages:
*/
-extern struct page *linux_alloc_pages(gfp_t flags, unsigned int order);
-extern void linux_free_pages(struct page *page, unsigned int order);
+struct page *linux_alloc_pages(gfp_t flags, unsigned int order);
+void linux_free_pages(struct page *page, unsigned int order);
void *linuxkpi_page_frag_alloc(struct page_frag_cache *, size_t, gfp_t);
void linuxkpi_page_frag_free(void *);
void linuxkpi__page_frag_cache_drain(struct page *, size_t);
@@ -143,11 +134,13 @@ dev_alloc_pages(unsigned int order)
return (linux_alloc_pages(GFP_ATOMIC, order));
}
+struct folio *folio_alloc(gfp_t gfp, unsigned int order);
+
/*
* Page management for mapped pages:
*/
-extern vm_offset_t linux_alloc_kmem(gfp_t flags, unsigned int order);
-extern void linux_free_kmem(vm_offset_t, unsigned int order);
+vm_offset_t linux_alloc_kmem(gfp_t flags, unsigned int order);
+void linux_free_kmem(vm_offset_t, unsigned int order);
static inline vm_offset_t
get_zeroed_page(gfp_t flags)
diff --git a/sys/compat/linuxkpi/common/include/linux/gpf.h b/sys/compat/linuxkpi/common/include/linux/gpf.h
new file mode 100644
index 000000000000..01e883a94728
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/gpf.h
@@ -0,0 +1,33 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 Serenity Cyber Security, LLC.
+ *
+ * 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_GPF_H_
+#define _LINUXKPI_LINUX_GPF_H_
+
+#include <linux/mmzone.h>
+
+#endif /* _LINUXKPI_LINUX_GPF_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/hdmi.h b/sys/compat/linuxkpi/common/include/linux/hdmi.h
index c8ec982ff498..e07578167d69 100644
--- a/sys/compat/linuxkpi/common/include/linux/hdmi.h
+++ b/sys/compat/linuxkpi/common/include/linux/hdmi.h
@@ -170,19 +170,19 @@ struct hdmi_avi_infoframe {
enum hdmi_infoframe_type type;
unsigned char version;
unsigned char length;
+ bool itc;
+ unsigned char pixel_repeat;
enum hdmi_colorspace colorspace;
enum hdmi_scan_mode scan_mode;
enum hdmi_colorimetry colorimetry;
enum hdmi_picture_aspect picture_aspect;
enum hdmi_active_aspect active_aspect;
- bool itc;
enum hdmi_extended_colorimetry extended_colorimetry;
enum hdmi_quantization_range quantization_range;
enum hdmi_nups nups;
unsigned char video_code;
enum hdmi_ycc_quantization_range ycc_quantization_range;
enum hdmi_content_type content_type;
- unsigned char pixel_repeat;
unsigned short top_bar;
unsigned short bottom_bar;
unsigned short left_bar;
@@ -336,7 +336,14 @@ ssize_t hdmi_audio_infoframe_pack(struct hdmi_audio_infoframe *frame,
void *buffer, size_t size);
ssize_t hdmi_audio_infoframe_pack_only(const struct hdmi_audio_infoframe *frame,
void *buffer, size_t size);
-int hdmi_audio_infoframe_check(struct hdmi_audio_infoframe *frame);
+int hdmi_audio_infoframe_check(const struct hdmi_audio_infoframe *frame);
+
+#ifdef __linux__
+struct dp_sdp;
+ssize_t
+hdmi_audio_infoframe_pack_for_dp(const struct hdmi_audio_infoframe *frame,
+ struct dp_sdp *sdp, u8 dp_version);
+#endif
enum hdmi_3d_structure {
HDMI_3D_STRUCTURE_INVALID = -1,
diff --git a/sys/compat/linuxkpi/common/include/linux/highmem.h b/sys/compat/linuxkpi/common/include/linux/highmem.h
index f770bef6b3b7..58a9cdcdf60f 100644
--- a/sys/compat/linuxkpi/common/include/linux/highmem.h
+++ b/sys/compat/linuxkpi/common/include/linux/highmem.h
@@ -43,6 +43,7 @@
#include <vm/vm_page.h>
#include <vm/pmap.h>
+#include <linux/mm.h>
#include <linux/page.h>
#define PageHighMem(p) (0)
@@ -78,9 +79,7 @@ kmap_atomic_prot(struct page *page, pgprot_t prot)
vm_memattr_t attr = pgprot2cachemode(prot);
if (attr != VM_MEMATTR_DEFAULT) {
- vm_page_lock(page);
page->flags |= PG_FICTITIOUS;
- vm_page_unlock(page);
pmap_page_set_memattr(page, attr);
}
return (kmap(page));
@@ -94,6 +93,12 @@ kmap_atomic(struct page *page)
}
static inline void *
+kmap_local_page(struct page *page)
+{
+ return (kmap(page));
+}
+
+static inline void *
kmap_local_page_prot(struct page *page, pgprot_t prot)
{
@@ -132,4 +137,34 @@ kunmap_local(void *addr)
kunmap_atomic(addr);
}
+static inline void
+memcpy_from_page(char *to, struct page *page, size_t offset, size_t len)
+{
+ char *from;
+
+ KASSERT(offset + len <= PAGE_SIZE,
+ ("%s: memcpy from page %p to address %p: "
+ "offset+len (%zu+%zu) would go beyond page end",
+ __func__, page, to, offset, len));
+
+ from = kmap_local_page(page);
+ memcpy(to, from + offset, len);
+ kunmap_local(from);
+}
+
+static inline void
+memcpy_to_page(struct page *page, size_t offset, const char *from, size_t len)
+{
+ char *to;
+
+ KASSERT(offset + len <= PAGE_SIZE,
+ ("%s: memcpy from address %p to page %p: "
+ "offset+len (%zu+%zu) would go beyond page end",
+ __func__, from, page, offset, len));
+
+ to = kmap_local_page(page);
+ memcpy(to + offset, from, len);
+ kunmap_local(to);
+}
+
#endif /* _LINUXKPI_LINUX_HIGHMEM_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/idr.h b/sys/compat/linuxkpi/common/include/linux/idr.h
index ca3f8171ff44..535d8ce07fb4 100644
--- a/sys/compat/linuxkpi/common/include/linux/idr.h
+++ b/sys/compat/linuxkpi/common/include/linux/idr.h
@@ -34,6 +34,8 @@
#include <sys/limits.h>
#include <sys/mutex.h>
+#include <linux/radix-tree.h>
+#include <linux/gpf.h>
#include <linux/types.h>
#define IDR_BITS 5
diff --git a/sys/compat/linuxkpi/common/include/linux/ieee80211.h b/sys/compat/linuxkpi/common/include/linux/ieee80211.h
index 2000e7480ff8..3644ef80861b 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-2023 The FreeBSD Foundation
+ * Copyright (c) 2020-2025 The FreeBSD Foundation
*
* This software was developed by Björn Zeeb under sponsorship from
* the FreeBSD Foundation.
@@ -33,9 +33,18 @@
#include <net80211/ieee80211.h>
#include <asm/unaligned.h>
+#include <linux/kernel.h>
#include <linux/bitops.h>
#include <linux/if_ether.h>
+/* linux_80211.c */
+extern int linuxkpi_debug_80211;
+#ifndef D80211_TODO
+#define D80211_TODO 0x1
+#endif
+#define TODO(fmt, ...) if (linuxkpi_debug_80211 & D80211_TODO) \
+ printf("%s:%d: XXX LKPI80211 TODO " fmt "\n", __func__, __LINE__, ##__VA_ARGS__)
+
/* 9.4.2.55 Management MIC element (CMAC-256, GMAC-128, and GMAC-256). */
struct ieee80211_mmie_16 {
@@ -55,6 +64,7 @@ struct ieee80211_mmie_16 {
#define IEEE80211_GCMP_MIC_LEN 16
#define IEEE80211_GCMP_PN_LEN 6
#define IEEE80211_GMAC_PN_LEN 6
+#define IEEE80211_CMAC_PN_LEN 6
#define IEEE80211_MAX_PN_LEN 16
@@ -91,7 +101,9 @@ struct ieee80211_mmie_16 {
#define IEEE80211_QOS_CTL_ACK_POLICY_NOACK 0x0020
#define IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT 0x0100
-#define IEEE80211_RATE_SHORT_PREAMBLE BIT(0)
+enum ieee80211_rate_flags {
+ IEEE80211_RATE_SHORT_PREAMBLE = BIT(0),
+};
enum ieee80211_rate_control_changed_flags {
IEEE80211_RC_BW_CHANGED = BIT(0),
@@ -106,7 +118,8 @@ enum ieee80211_rate_control_changed_flags {
#define IEEE80211_TKIP_ICV_LEN 4
#define IEEE80211_TKIP_IV_LEN 8 /* WEP + KID + EXT */
-#define IEEE80211_VHT_EXT_NSS_BW_CAPABLE (1 << 13) /* assigned to tx_highest */
+/* 802.11-2016, 9.4.2.158.3 Supported VHT-MCS and NSS Set field. */
+#define IEEE80211_VHT_EXT_NSS_BW_CAPABLE (1 << 13) /* part of tx_highest */
#define IEEE80211_VHT_MAX_AMPDU_1024K 7 /* 9.4.2.56.3 A-MPDU Parameters field, Table 9-163 */
@@ -125,19 +138,23 @@ enum wlan_ht_cap_sm_ps {
WLAN_HT_CAP_SM_PS_DISABLED,
};
-#define WLAN_MAX_KEY_LEN 32 /* TODO FIXME brcmfmac */
-#define WLAN_PMKID_LEN 16 /* TODO FIXME brcmfmac */
-
-#define WLAN_KEY_LEN_WEP40 5
-#define WLAN_KEY_LEN_WEP104 13
-#define WLAN_KEY_LEN_TKIP 32
-#define WLAN_KEY_LEN_CCMP 16
-#define WLAN_KEY_LEN_GCMP 16
-#define WLAN_KEY_LEN_AES_CMAC 16
-#define WLAN_KEY_LEN_GCMP_256 32
-#define WLAN_KEY_LEN_BIP_CMAC_256 32
-#define WLAN_KEY_LEN_BIP_GMAC_128 16
-#define WLAN_KEY_LEN_BIP_GMAC_256 32
+#define WLAN_MAX_KEY_LEN 32
+#define WLAN_PMKID_LEN 16
+#define WLAN_PMK_LEN_SUITE_B_192 48
+
+enum ieee80211_key_len {
+ WLAN_KEY_LEN_WEP40 = 5,
+ WLAN_KEY_LEN_WEP104 = 13,
+ WLAN_KEY_LEN_TKIP = 32,
+ WLAN_KEY_LEN_CCMP = 16,
+ WLAN_KEY_LEN_CCMP_256 = 32,
+ WLAN_KEY_LEN_GCMP = 16,
+ WLAN_KEY_LEN_AES_CMAC = 16,
+ WLAN_KEY_LEN_GCMP_256 = 32,
+ WLAN_KEY_LEN_BIP_CMAC_256 = 32,
+ WLAN_KEY_LEN_BIP_GMAC_128 = 16,
+ WLAN_KEY_LEN_BIP_GMAC_256 = 32,
+};
/* 802.11-2020, 9.4.2.55.3, Table 9-185 Subfields of the A-MPDU Parameters field */
enum ieee80211_min_mpdu_start_spacing {
@@ -178,6 +195,7 @@ enum ieee80211_min_mpdu_start_spacing {
#define IEEE80211_STYPE_CTS IEEE80211_FC0_SUBTYPE_CTS
#define IEEE80211_STYPE_RTS IEEE80211_FC0_SUBTYPE_RTS
#define IEEE80211_STYPE_ACTION IEEE80211_FC0_SUBTYPE_ACTION
+#define IEEE80211_STYPE_DATA IEEE80211_FC0_SUBTYPE_DATA
#define IEEE80211_STYPE_QOS_DATA IEEE80211_FC0_SUBTYPE_QOS_DATA
#define IEEE80211_STYPE_QOS_NULLFUNC IEEE80211_FC0_SUBTYPE_QOS_NULL
#define IEEE80211_STYPE_QOS_CFACK 0xd0 /* XXX-BZ reserved? */
@@ -254,20 +272,20 @@ enum ieee80211_ac_numbers {
#define IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL 0xf
-/* XXX net80211 calls these IEEE80211_HTCAP_* */
-#define IEEE80211_HT_CAP_LDPC_CODING 0x0001 /* IEEE80211_HTCAP_LDPC */
-#define IEEE80211_HT_CAP_SUP_WIDTH_20_40 0x0002 /* IEEE80211_HTCAP_CHWIDTH40 */
-#define IEEE80211_HT_CAP_SM_PS 0x000c /* IEEE80211_HTCAP_SMPS */
+/* Define the LinuxKPI names directly to the net80211 ones. */
+#define IEEE80211_HT_CAP_LDPC_CODING IEEE80211_HTCAP_LDPC
+#define IEEE80211_HT_CAP_SUP_WIDTH_20_40 IEEE80211_HTCAP_CHWIDTH40
+#define IEEE80211_HT_CAP_SM_PS IEEE80211_HTCAP_SMPS
#define IEEE80211_HT_CAP_SM_PS_SHIFT 2
-#define IEEE80211_HT_CAP_GRN_FLD 0x0010 /* IEEE80211_HTCAP_GREENFIELD */
-#define IEEE80211_HT_CAP_SGI_20 0x0020 /* IEEE80211_HTCAP_SHORTGI20 */
-#define IEEE80211_HT_CAP_SGI_40 0x0040 /* IEEE80211_HTCAP_SHORTGI40 */
-#define IEEE80211_HT_CAP_TX_STBC 0x0080 /* IEEE80211_HTCAP_TXSTBC */
-#define IEEE80211_HT_CAP_RX_STBC 0x0100 /* IEEE80211_HTCAP_RXSTBC */
-#define IEEE80211_HT_CAP_RX_STBC_SHIFT 8 /* IEEE80211_HTCAP_RXSTBC_S */
-#define IEEE80211_HT_CAP_MAX_AMSDU 0x0800 /* IEEE80211_HTCAP_MAXAMSDU */
-#define IEEE80211_HT_CAP_DSSSCCK40 0x1000 /* IEEE80211_HTCAP_DSSSCCK40 */
-#define IEEE80211_HT_CAP_LSIG_TXOP_PROT 0x8000 /* IEEE80211_HTCAP_LSIGTXOPPROT */
+#define IEEE80211_HT_CAP_GRN_FLD IEEE80211_HTCAP_GREENFIELD
+#define IEEE80211_HT_CAP_SGI_20 IEEE80211_HTCAP_SHORTGI20
+#define IEEE80211_HT_CAP_SGI_40 IEEE80211_HTCAP_SHORTGI40
+#define IEEE80211_HT_CAP_TX_STBC IEEE80211_HTCAP_TXSTBC
+#define IEEE80211_HT_CAP_RX_STBC IEEE80211_HTCAP_RXSTBC
+#define IEEE80211_HT_CAP_RX_STBC_SHIFT IEEE80211_HTCAP_RXSTBC_S
+#define IEEE80211_HT_CAP_MAX_AMSDU IEEE80211_HTCAP_MAXAMSDU
+#define IEEE80211_HT_CAP_DSSSCCK40 IEEE80211_HTCAP_DSSSCCK40
+#define IEEE80211_HT_CAP_LSIG_TXOP_PROT IEEE80211_HTCAP_LSIGTXOPPROT
#define IEEE80211_HT_MCS_TX_DEFINED 0x0001
#define IEEE80211_HT_MCS_TX_RX_DIFF 0x0002
@@ -277,13 +295,16 @@ enum ieee80211_ac_numbers {
#define IEEE80211_HT_MCS_MASK_LEN 10
#define IEEE80211_MLD_MAX_NUM_LINKS 15
+#define IEEE80211_MLD_CAP_OP_MAX_SIMUL_LINKS 0xf
+#define IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP 0x0060
+#define IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP_SAME 1
struct ieee80211_mcs_info {
uint8_t rx_mask[IEEE80211_HT_MCS_MASK_LEN];
uint16_t rx_highest;
uint8_t tx_params;
uint8_t __reserved[3];
-};
+} __packed;
/* 802.11-2020, 9.4.2.55.1 HT Capabilities element structure */
struct ieee80211_ht_cap {
@@ -293,7 +314,7 @@ struct ieee80211_ht_cap {
uint16_t extended_ht_cap_info;
uint32_t tx_BF_cap_info;
uint8_t antenna_selection_info;
-};
+} __packed;
#define IEEE80211_HT_MAX_AMPDU_FACTOR 13
#define IEEE80211_HE_HT_MAX_AMPDU_FACTOR 16
@@ -328,6 +349,8 @@ enum ieee80211_chanctx_change_flags {
IEEE80211_CHANCTX_CHANGE_RX_CHAINS = BIT(2),
IEEE80211_CHANCTX_CHANGE_WIDTH = BIT(3),
IEEE80211_CHANCTX_CHANGE_CHANNEL = BIT(4),
+ IEEE80211_CHANCTX_CHANGE_PUNCTURING = BIT(5),
+ IEEE80211_CHANCTX_CHANGE_MIN_DEF = BIT(6),
};
enum ieee80211_frame_release_type {
@@ -371,14 +394,6 @@ enum ieee80211_sta_state {
IEEE80211_STA_AUTHORIZED = 4, /* 802.1x */
};
-enum ieee80211_sta_rx_bw {
- IEEE80211_STA_RX_BW_20,
- IEEE80211_STA_RX_BW_40,
- IEEE80211_STA_RX_BW_80,
- IEEE80211_STA_RX_BW_160,
- IEEE80211_STA_RX_BW_320,
-};
-
enum ieee80211_tx_info_flags {
/* XXX TODO .. right shift numbers - not sure where that came from? */
IEEE80211_TX_CTL_AMPDU = BIT(0),
@@ -402,7 +417,7 @@ enum ieee80211_tx_info_flags {
IEEE80211_TX_CTL_RATE_CTRL_PROBE = BIT(18),
IEEE80211_TX_CTL_LDPC = BIT(19),
IEEE80211_TX_CTL_STBC = BIT(20),
-};
+} __packed;
enum ieee80211_tx_status_flags {
IEEE80211_TX_STATUS_ACK_SIGNAL_VALID = BIT(0),
@@ -413,6 +428,7 @@ enum ieee80211_tx_control_flags {
IEEE80211_TX_CTRL_PORT_CTRL_PROTO = BIT(0),
IEEE80211_TX_CTRL_PS_RESPONSE = BIT(1),
IEEE80211_TX_CTRL_RATE_INJECT = BIT(2),
+ IEEE80211_TX_CTRL_DONT_USE_RATE_MASK = BIT(3),
IEEE80211_TX_CTRL_MLO_LINK = 0xF0000000, /* This is IEEE80211_LINK_UNSPECIFIED on the high bits. */
};
@@ -469,9 +485,14 @@ enum ieee80211_back {
WLAN_ACTION_ADDBA_REQ = 0,
};
+enum ieee80211_sa_query {
+ WLAN_ACTION_SA_QUERY_RESPONSE = 1,
+};
+
/* 802.11-2020, Table 9-51-Category values */
enum ieee80211_category {
WLAN_CATEGORY_BACK = 3,
+ WLAN_CATEGORY_SA_QUERY = 8, /* net80211::IEEE80211_ACTION_CAT_SA_QUERY */
};
/* 80211-2020 9.3.3.2 Format of Management frames */
@@ -490,6 +511,12 @@ struct ieee80211_mgmt {
uint16_t capab_info;
uint8_t variable[0];
} beacon;
+ /* 9.3.3.5 Association Request frame format */
+ struct {
+ uint16_t capab_info;
+ uint16_t listen_interval;
+ uint8_t variable[0];
+ } assoc_req;
/* 9.3.3.10 Probe Request frame format */
struct {
uint8_t variable[0];
@@ -543,6 +570,7 @@ struct ieee80211_mgmt {
} wnm_timing_msr;
} u;
} action;
+ DECLARE_FLEX_ARRAY(uint8_t, body);
} u;
};
@@ -567,6 +595,8 @@ struct ieee80211_rts { /* net80211::ieee80211_frame_rts */
#define IEEE80211_SEQ_TO_SN(_seqn) (((_seqn) & IEEE80211_SEQ_SEQ_MASK) >> \
IEEE80211_SEQ_SEQ_SHIFT)
+#define IEEE80211_SN_TO_SEQ(_sn) (((_sn) << IEEE80211_SEQ_SEQ_SHIFT) & \
+ IEEE80211_SEQ_SEQ_MASK)
/* Time unit (TU) to .. See net80211: IEEE80211_DUR_TU */
#define TU_TO_JIFFIES(_tu) (usecs_to_jiffies(_tu) * 1024)
@@ -584,6 +614,7 @@ enum ieee80211_eid {
WLAN_EID_TIM = 5,
WLAN_EID_COUNTRY = 7, /* IEEE80211_ELEMID_COUNTRY */
WLAN_EID_REQUEST = 10,
+ WLAN_EID_QBSS_LOAD = 11, /* IEEE80211_ELEMID_BSSLOAD */
WLAN_EID_CHANNEL_SWITCH = 37,
WLAN_EID_MEASURE_REPORT = 39,
WLAN_EID_HT_CAPABILITY = 45, /* IEEE80211_ELEMID_HTCAP */
@@ -595,6 +626,7 @@ enum ieee80211_eid {
WLAN_EID_MULTI_BSSID_IDX = 85,
WLAN_EID_EXT_CAPABILITY = 127,
WLAN_EID_VHT_CAPABILITY = 191, /* IEEE80211_ELEMID_VHT_CAP */
+ WLAN_EID_S1G_TWT = 216,
WLAN_EID_VENDOR_SPECIFIC = 221, /* IEEE80211_ELEMID_VENDOR */
};
@@ -640,10 +672,10 @@ struct ieee80211_trigger {
/* Table 9-29c-Trigger Type subfield encoding */
enum {
IEEE80211_TRIGGER_TYPE_BASIC = 0x0,
+ IEEE80211_TRIGGER_TYPE_MU_BAR = 0x2,
#if 0
/* Not seen yet. */
BFRP = 0x1,
- MU-BAR = 0x2,
MU-RTS = 0x3,
BSRP = 0x4,
GCR MU-BAR = 0x5,
@@ -654,6 +686,12 @@ enum {
IEEE80211_TRIGGER_TYPE_MASK = 0xf
};
+#define IEEE80211_TRIGGER_ULBW_MASK 0xc0000
+#define IEEE80211_TRIGGER_ULBW_20MHZ 0x0
+#define IEEE80211_TRIGGER_ULBW_40MHZ 0x1
+#define IEEE80211_TRIGGER_ULBW_80MHZ 0x2
+#define IEEE80211_TRIGGER_ULBW_160_80P80MHZ 0x3
+
/* 802.11-2020, Figure 9-687-Control field format; 802.11ax-2021 */
#define IEEE80211_TWT_CONTROL_NEG_TYPE_BROADCAST BIT(3)
#define IEEE80211_TWT_CONTROL_RX_DISABLED BIT(4)
@@ -694,12 +732,74 @@ struct ieee80211_bssid_index {
int bssid_index;
};
-enum ieee80211_reg_ap_power {
+enum ieee80211_ap_reg_power {
+ IEEE80211_REG_UNSET_AP,
IEEE80211_REG_LPI_AP,
IEEE80211_REG_SP_AP,
IEEE80211_REG_VLP_AP,
};
+/*
+ * 802.11ax-2021, Table 9-277-Meaning of Maximum Transmit Power Count subfield
+ * if Maximum Transmit Power Interpretation subfield is 1 or 3
+ */
+#define IEEE80211_MAX_NUM_PWR_LEVEL 8
+
+/*
+ * 802.11ax-2021, Table 9-275a-Maximum Transmit Power Interpretation subfield
+ * encoding (4) * Table E-12-Regulatory Info subfield encoding in the
+ * United States (2)
+ */
+#define IEEE80211_TPE_MAX_IE_NUM 8
+
+/* 802.11ax-2021, 9.4.2.161 Transmit Power Envelope element */
+struct ieee80211_tx_pwr_env {
+ uint8_t tx_power_info;
+ uint8_t tx_power[IEEE80211_MAX_NUM_PWR_LEVEL];
+};
+
+/* 802.11ax-2021, Figure 9-617-Transmit Power Information field format */
+/* These are field masks (3bit/3bit/2bit). */
+#define IEEE80211_TX_PWR_ENV_INFO_COUNT 0x07
+#define IEEE80211_TX_PWR_ENV_INFO_INTERPRET 0x38
+#define IEEE80211_TX_PWR_ENV_INFO_CATEGORY 0xc0
+
+/*
+ * 802.11ax-2021, Table 9-275a-Maximum Transmit Power Interpretation subfield
+ * encoding
+ */
+enum ieee80211_tx_pwr_interpretation_subfield_enc {
+ IEEE80211_TPE_LOCAL_EIRP,
+ IEEE80211_TPE_LOCAL_EIRP_PSD,
+ IEEE80211_TPE_REG_CLIENT_EIRP,
+ IEEE80211_TPE_REG_CLIENT_EIRP_PSD,
+};
+
+enum ieee80211_tx_pwr_category_6ghz {
+ IEEE80211_TPE_CAT_6GHZ_DEFAULT,
+};
+
+/* 802.11-2020, 9.4.2.27 BSS Load element */
+struct ieee80211_bss_load_elem {
+ uint16_t sta_count;
+ uint8_t channel_util;
+ uint16_t avail_adm_capa;
+};
+
+struct ieee80211_p2p_noa_desc {
+ uint32_t count; /* uint8_t ? */
+ uint32_t duration;
+ uint32_t interval;
+ uint32_t start_time;
+};
+
+struct ieee80211_p2p_noa_attr {
+ uint8_t index;
+ uint8_t oppps_ctwindow;
+ struct ieee80211_p2p_noa_desc desc[4];
+};
+
+
/* net80211: IEEE80211_IS_CTL() */
static __inline bool
ieee80211_is_ctl(__le16 fc)
@@ -800,4 +900,346 @@ ieee80211_is_trigger(__le16 fc)
return (fc == v);
}
+static __inline bool
+ieee80211_is_action(__le16 fc)
+{
+ __le16 v;
+
+ fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK);
+ v = htole16(IEEE80211_FC0_SUBTYPE_ACTION | IEEE80211_FC0_TYPE_MGT);
+
+ return (fc == v);
+}
+
+static __inline bool
+ieee80211_is_probe_resp(__le16 fc)
+{
+ __le16 v;
+
+ fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK);
+ v = htole16(IEEE80211_FC0_SUBTYPE_PROBE_RESP | IEEE80211_FC0_TYPE_MGT);
+
+ return (fc == v);
+}
+
+static __inline bool
+ieee80211_is_auth(__le16 fc)
+{
+ __le16 v;
+
+ fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK);
+ v = htole16(IEEE80211_FC0_SUBTYPE_AUTH | IEEE80211_FC0_TYPE_MGT);
+
+ return (fc == v);
+}
+
+static __inline bool
+ieee80211_is_assoc_req(__le16 fc)
+{
+ __le16 v;
+
+ fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK);
+ v = htole16(IEEE80211_FC0_SUBTYPE_ASSOC_REQ | IEEE80211_FC0_TYPE_MGT);
+
+ return (fc == v);
+}
+
+static __inline bool
+ieee80211_is_assoc_resp(__le16 fc)
+{
+ __le16 v;
+
+ fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK);
+ v = htole16(IEEE80211_FC0_SUBTYPE_ASSOC_RESP | IEEE80211_FC0_TYPE_MGT);
+
+ return (fc == v);
+}
+
+static __inline bool
+ieee80211_is_reassoc_req(__le16 fc)
+{
+ __le16 v;
+
+ fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK);
+ v = htole16(IEEE80211_FC0_SUBTYPE_REASSOC_REQ | IEEE80211_FC0_TYPE_MGT);
+
+ return (fc == v);
+}
+
+static __inline bool
+ieee80211_is_reassoc_resp(__le16 fc)
+{
+ __le16 v;
+
+ fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK);
+ v = htole16(IEEE80211_FC0_SUBTYPE_REASSOC_RESP | IEEE80211_FC0_TYPE_MGT);
+
+ return (fc == v);
+}
+
+static __inline bool
+ieee80211_is_disassoc(__le16 fc)
+{
+ __le16 v;
+
+ fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK);
+ v = htole16(IEEE80211_FC0_SUBTYPE_DISASSOC | IEEE80211_FC0_TYPE_MGT);
+
+ return (fc == v);
+}
+
+static __inline bool
+ieee80211_is_data_present(__le16 fc)
+{
+ __le16 v;
+
+ /* If it is a data frame and NODATA is not present. */
+ fc &= htole16(IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_NODATA);
+ v = htole16(IEEE80211_FC0_TYPE_DATA);
+
+ return (fc == v);
+}
+
+static __inline bool
+ieee80211_is_deauth(__le16 fc)
+{
+ __le16 v;
+
+ fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK);
+ v = htole16(IEEE80211_FC0_SUBTYPE_DEAUTH | IEEE80211_FC0_TYPE_MGT);
+
+ return (fc == v);
+}
+
+static __inline bool
+ieee80211_is_beacon(__le16 fc)
+{
+ __le16 v;
+
+ /*
+ * For as much as I get it this comes in LE and unlike FreeBSD
+ * where we get the entire frame header and u8[], here we get the
+ * 9.2.4.1 Frame Control field only. Mask and compare.
+ */
+ fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK);
+ v = htole16(IEEE80211_FC0_SUBTYPE_BEACON | IEEE80211_FC0_TYPE_MGT);
+
+ return (fc == v);
+}
+
+
+static __inline bool
+ieee80211_is_probe_req(__le16 fc)
+{
+ __le16 v;
+
+ fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK);
+ v = htole16(IEEE80211_FC0_SUBTYPE_PROBE_REQ | IEEE80211_FC0_TYPE_MGT);
+
+ return (fc == v);
+}
+
+static __inline bool
+ieee80211_has_protected(__le16 fc)
+{
+
+ return (fc & htole16(IEEE80211_FC1_PROTECTED << 8));
+}
+
+static __inline bool
+ieee80211_is_back_req(__le16 fc)
+{
+ __le16 v;
+
+ fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK);
+ v = htole16(IEEE80211_FC0_SUBTYPE_BAR | IEEE80211_FC0_TYPE_CTL);
+
+ return (fc == v);
+}
+
+static __inline bool
+ieee80211_is_bufferable_mmpdu(struct sk_buff *skb)
+{
+ struct ieee80211_mgmt *mgmt;
+ __le16 fc;
+
+ mgmt = (struct ieee80211_mgmt *)skb->data;
+ fc = mgmt->frame_control;
+
+ /* 11.2.2 Bufferable MMPDUs, 80211-2020. */
+ /* XXX we do not care about IBSS yet. */
+
+ if (!ieee80211_is_mgmt(fc))
+ return (false);
+ if (ieee80211_is_action(fc)) /* XXX FTM? */
+ return (true); /* XXX false? */
+ if (ieee80211_is_disassoc(fc))
+ return (true);
+ if (ieee80211_is_deauth(fc))
+ return (true);
+
+ TODO();
+
+ return (false);
+}
+
+static __inline bool
+ieee80211_is_nullfunc(__le16 fc)
+{
+ __le16 v;
+
+ fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK);
+ v = htole16(IEEE80211_FC0_SUBTYPE_NODATA | IEEE80211_FC0_TYPE_DATA);
+
+ return (fc == v);
+}
+
+static __inline bool
+ieee80211_is_qos_nullfunc(__le16 fc)
+{
+ __le16 v;
+
+ fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK);
+ v = htole16(IEEE80211_FC0_SUBTYPE_QOS_NULL | IEEE80211_FC0_TYPE_DATA);
+
+ return (fc == v);
+}
+
+static __inline bool
+ieee80211_is_any_nullfunc(__le16 fc)
+{
+
+ return (ieee80211_is_nullfunc(fc) || ieee80211_is_qos_nullfunc(fc));
+}
+
+static inline bool
+ieee80211_is_pspoll(__le16 fc)
+{
+ __le16 v;
+
+ fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK);
+ v = htole16(IEEE80211_FC0_SUBTYPE_PS_POLL | IEEE80211_FC0_TYPE_CTL);
+
+ return (fc == v);
+}
+
+static __inline bool
+ieee80211_has_a4(__le16 fc)
+{
+ __le16 v;
+
+ fc &= htole16((IEEE80211_FC1_DIR_TODS | IEEE80211_FC1_DIR_FROMDS) << 8);
+ v = htole16((IEEE80211_FC1_DIR_TODS | IEEE80211_FC1_DIR_FROMDS) << 8);
+
+ return (fc == v);
+}
+
+static __inline bool
+ieee80211_has_order(__le16 fc)
+{
+
+ return (fc & htole16(IEEE80211_FC1_ORDER << 8));
+}
+
+static __inline bool
+ieee80211_has_retry(__le16 fc)
+{
+
+ return (fc & htole16(IEEE80211_FC1_RETRY << 8));
+}
+
+
+static __inline bool
+ieee80211_has_fromds(__le16 fc)
+{
+
+ return (fc & htole16(IEEE80211_FC1_DIR_FROMDS << 8));
+}
+
+static __inline bool
+ieee80211_has_tods(__le16 fc)
+{
+
+ return (fc & htole16(IEEE80211_FC1_DIR_TODS << 8));
+}
+
+static __inline uint8_t *
+ieee80211_get_SA(struct ieee80211_hdr *hdr)
+{
+
+ if (ieee80211_has_a4(hdr->frame_control))
+ return (hdr->addr4);
+ if (ieee80211_has_fromds(hdr->frame_control))
+ return (hdr->addr3);
+ return (hdr->addr2);
+}
+
+static __inline uint8_t *
+ieee80211_get_DA(struct ieee80211_hdr *hdr)
+{
+
+ if (ieee80211_has_tods(hdr->frame_control))
+ return (hdr->addr3);
+ return (hdr->addr1);
+}
+
+static __inline bool
+ieee80211_is_frag(struct ieee80211_hdr *hdr)
+{
+ TODO();
+ return (false);
+}
+
+static __inline bool
+ieee80211_is_first_frag(__le16 fc)
+{
+ TODO();
+ return (false);
+}
+
+static __inline bool
+ieee80211_is_robust_mgmt_frame(struct sk_buff *skb)
+{
+ TODO();
+ return (false);
+}
+
+static __inline bool
+ieee80211_is_ftm(struct sk_buff *skb)
+{
+ TODO();
+ return (false);
+}
+
+static __inline bool
+ieee80211_is_timing_measurement(struct sk_buff *skb)
+{
+ TODO();
+ return (false);
+}
+
+static __inline bool
+ieee80211_has_pm(__le16 fc)
+{
+ TODO();
+ return (false);
+}
+
+static __inline bool
+ieee80211_has_morefrags(__le16 fc)
+{
+
+ fc &= htole16(IEEE80211_FC1_MORE_FRAG << 8);
+ return (fc != 0);
+}
+
+static __inline u8 *
+ieee80211_get_qos_ctl(struct ieee80211_hdr *hdr)
+{
+ if (ieee80211_has_a4(hdr->frame_control))
+ return (u8 *)hdr + 30;
+ else
+ return (u8 *)hdr + 24;
+}
+
#endif /* _LINUXKPI_LINUX_IEEE80211_H */
diff --git a/sys/compat/linuxkpi/common/include/linux/if_ether.h b/sys/compat/linuxkpi/common/include/linux/if_ether.h
index 3735ad2f5527..6676e8fc142f 100644
--- a/sys/compat/linuxkpi/common/include/linux/if_ether.h
+++ b/sys/compat/linuxkpi/common/include/linux/if_ether.h
@@ -34,6 +34,7 @@
#define _LINUXKPI_LINUX_IF_ETHER_H_
#include <linux/types.h>
+#include <linux/skbuff.h>
#include <net/ethernet.h>
@@ -69,4 +70,13 @@ struct ethhdr {
uint16_t h_proto;
} __packed;
+static inline struct ethhdr *
+eth_hdr(const struct sk_buff *skb)
+{
+ struct ethhdr *hdr;
+
+ hdr = (struct ethhdr *)skb_mac_header(skb);
+ return (hdr);
+}
+
#endif /* _LINUXKPI_LINUX_IF_ETHER_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/interrupt.h b/sys/compat/linuxkpi/common/include/linux/interrupt.h
index d5f9a0ae7a47..dfd9816da8be 100644
--- a/sys/compat/linuxkpi/common/include/linux/interrupt.h
+++ b/sys/compat/linuxkpi/common/include/linux/interrupt.h
@@ -133,7 +133,7 @@ irq_set_affinity_hint(int vector, const cpumask_t *mask)
int error;
if (mask != NULL)
- error = intr_setaffinity(vector, CPU_WHICH_IRQ, __DECONST(cpumask_t *, mask));
+ error = intr_setaffinity(vector, CPU_WHICH_IRQ, mask);
else
error = intr_setaffinity(vector, CPU_WHICH_IRQ, cpuset_root);
diff --git a/sys/compat/linuxkpi/common/include/linux/io.h b/sys/compat/linuxkpi/common/include/linux/io.h
index bce70ed0cb8d..2d6fef4e7c52 100644
--- a/sys/compat/linuxkpi/common/include/linux/io.h
+++ b/sys/compat/linuxkpi/common/include/linux/io.h
@@ -36,6 +36,7 @@
#include <linux/compiler.h>
#include <linux/err.h>
+#include <asm-generic/io.h>
#include <linux/types.h>
#if !defined(__arm__)
#include <asm/set_memory.h>
@@ -395,11 +396,7 @@ iowrite32be(uint32_t v, volatile void *addr)
#define iowrite32be(v, addr) iowrite32be(v, addr)
#if defined(__i386__) || defined(__amd64__)
-static inline void
-_outb(u_char data, u_int port)
-{
- __asm __volatile("outb %0, %w1" : : "a" (data), "Nd" (port));
-}
+#define _outb(data, port) outb((data), (port))
#endif
#if defined(__i386__) || defined(__amd64__) || defined(__powerpc__) || defined(__aarch64__) || defined(__riscv)
@@ -541,30 +538,29 @@ void lkpi_arch_phys_wc_del(int);
#define arch_phys_wc_index(x) \
(((x) < __MTRR_ID_BASE) ? -1 : ((x) - __MTRR_ID_BASE))
-#if defined(__amd64__) || defined(__i386__) || defined(__aarch64__) || defined(__powerpc__) || defined(__riscv)
static inline int
arch_io_reserve_memtype_wc(resource_size_t start, resource_size_t size)
{
+#if defined(__amd64__)
vm_offset_t va;
va = PHYS_TO_DMAP(start);
-
-#ifdef VM_MEMATTR_WRITE_COMBINING
return (-pmap_change_attr(va, size, VM_MEMATTR_WRITE_COMBINING));
#else
- return (-pmap_change_attr(va, size, VM_MEMATTR_UNCACHEABLE));
+ return (0);
#endif
}
static inline void
arch_io_free_memtype_wc(resource_size_t start, resource_size_t size)
{
+#if defined(__amd64__)
vm_offset_t va;
va = PHYS_TO_DMAP(start);
pmap_change_attr(va, size, VM_MEMATTR_WRITE_BACK);
-}
#endif
+}
#endif /* _LINUXKPI_LINUX_IO_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/jiffies.h b/sys/compat/linuxkpi/common/include/linux/jiffies.h
index d5cb6e14860e..c2409726e874 100644
--- a/sys/compat/linuxkpi/common/include/linux/jiffies.h
+++ b/sys/compat/linuxkpi/common/include/linux/jiffies.h
@@ -32,26 +32,27 @@
#include <linux/types.h>
#include <linux/time.h>
-#include <sys/time.h>
#include <sys/kernel.h>
#include <sys/limits.h>
+#include <sys/time.h>
-#define jiffies ticks
-#define jiffies_64 ticks
+extern unsigned long jiffies; /* defined in sys/kern/subr_ticks.S */
+#define jiffies_64 jiffies /* XXX-MJ wrong on 32-bit platforms */
#define jiffies_to_msecs(x) ((unsigned int)(((int64_t)(int)(x)) * 1000 / hz))
-#define MAX_JIFFY_OFFSET ((INT_MAX >> 1) - 1)
+#define MAX_JIFFY_OFFSET ((LONG_MAX >> 1) - 1)
-#define time_after(a, b) ((int)((b) - (a)) < 0)
+#define time_after(a, b) ((long)((b) - (a)) < 0)
#define time_after32(a, b) ((int32_t)((uint32_t)(b) - (uint32_t)(a)) < 0)
#define time_before(a, b) time_after(b,a)
#define time_before32(a, b) time_after32(b, a)
-#define time_after_eq(a, b) ((int)((a) - (b)) >= 0)
+#define time_after_eq(a, b) ((long)((a) - (b)) >= 0)
#define time_before_eq(a, b) time_after_eq(b, a)
#define time_in_range(a,b,c) \
(time_after_eq(a,b) && time_before_eq(a,c))
#define time_is_after_eq_jiffies(a) time_after_eq(a, jiffies)
#define time_is_after_jiffies(a) time_after(a, jiffies)
+#define time_is_before_jiffies(a) time_before(a, jiffies)
#define HZ hz
@@ -67,20 +68,7 @@ extern uint64_t lkpi_msec2hz_rem;
extern uint64_t lkpi_msec2hz_div;
extern uint64_t lkpi_msec2hz_max;
-static inline int
-timespec_to_jiffies(const struct timespec *ts)
-{
- u64 result;
-
- result = ((u64)hz * ts->tv_sec) +
- (((u64)hz * ts->tv_nsec + NSEC_PER_SEC - 1) / NSEC_PER_SEC);
- if (result > MAX_JIFFY_OFFSET)
- result = MAX_JIFFY_OFFSET;
-
- return ((int)result);
-}
-
-static inline int
+static inline unsigned long
msecs_to_jiffies(uint64_t msec)
{
uint64_t result;
@@ -91,10 +79,10 @@ msecs_to_jiffies(uint64_t msec)
if (result > MAX_JIFFY_OFFSET)
result = MAX_JIFFY_OFFSET;
- return ((int)result);
+ return ((unsigned long)result);
}
-static inline int
+static inline unsigned long
usecs_to_jiffies(uint64_t usec)
{
uint64_t result;
@@ -105,7 +93,7 @@ usecs_to_jiffies(uint64_t usec)
if (result > MAX_JIFFY_OFFSET)
result = MAX_JIFFY_OFFSET;
- return ((int)result);
+ return ((unsigned long)result);
}
static inline uint64_t
@@ -132,34 +120,24 @@ nsecs_to_jiffies(uint64_t nsec)
}
static inline uint64_t
-jiffies_to_nsecs(int j)
+jiffies_to_nsecs(unsigned long j)
{
- return ((1000000000ULL / hz) * (uint64_t)(unsigned int)j);
+ return ((1000000000ULL / hz) * (uint64_t)j);
}
static inline uint64_t
-jiffies_to_usecs(int j)
+jiffies_to_usecs(unsigned long j)
{
- return ((1000000ULL / hz) * (uint64_t)(unsigned int)j);
+ return ((1000000ULL / hz) * (uint64_t)j);
}
static inline uint64_t
get_jiffies_64(void)
{
- return ((uint64_t)(unsigned int)ticks);
-}
-
-static inline int
-linux_timer_jiffies_until(int expires)
-{
- int delta = expires - jiffies;
- /* guard against already expired values */
- if (delta < 1)
- delta = 1;
- return (delta);
+ return ((uint64_t)jiffies);
}
#endif /* _LINUXKPI_LINUX_JIFFIES_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/kernel.h b/sys/compat/linuxkpi/common/include/linux/kernel.h
index ed4320e80fa7..11a13cbd49b4 100644
--- a/sys/compat/linuxkpi/common/include/linux/kernel.h
+++ b/sys/compat/linuxkpi/common/include/linux/kernel.h
@@ -44,10 +44,12 @@
#include <linux/build_bug.h>
#include <linux/compiler.h>
#include <linux/container_of.h>
+#include <linux/kstrtox.h>
#include <linux/limits.h>
+#include <linux/math.h>
+#include <linux/minmax.h>
#include <linux/stringify.h>
#include <linux/errno.h>
-#include <linux/sched.h>
#include <linux/types.h>
#include <linux/typecheck.h>
#include <linux/jiffies.h>
@@ -123,10 +125,7 @@ extern int linuxkpi_warn_dump_stack;
#undef PTR_ALIGN
#define PTR_ALIGN(p, a) ((__typeof(p))ALIGN((uintptr_t)(p), (a)))
#define IS_ALIGNED(x, a) (((x) & ((__typeof(x))(a) - 1)) == 0)
-#define DIV_ROUND_UP(x, n) howmany(x, n)
#define __KERNEL_DIV_ROUND_UP(x, n) howmany(x, n)
-#define DIV_ROUND_UP_ULL(x, n) DIV_ROUND_UP((unsigned long long)(x), (n))
-#define DIV_ROUND_DOWN_ULL(x, n) (((unsigned long long)(x) / (n)) * (n))
#define FIELD_SIZEOF(t, f) sizeof(((t *)0)->f)
#define printk(...) printf(__VA_ARGS__)
@@ -270,286 +269,9 @@ extern int linuxkpi_debug;
#define _RET_IP_ __builtin_return_address(0)
-static inline unsigned long long
-simple_strtoull(const char *cp, char **endp, unsigned int base)
-{
- return (strtouq(cp, endp, base));
-}
-
-static inline long long
-simple_strtoll(const char *cp, char **endp, unsigned int base)
-{
- return (strtoq(cp, endp, base));
-}
-
-static inline unsigned long
-simple_strtoul(const char *cp, char **endp, unsigned int base)
-{
- return (strtoul(cp, endp, base));
-}
-
-static inline long
-simple_strtol(const char *cp, char **endp, unsigned int base)
-{
- return (strtol(cp, endp, base));
-}
-
-static inline int
-kstrtoul(const char *cp, unsigned int base, unsigned long *res)
-{
- char *end;
-
- *res = strtoul(cp, &end, base);
-
- /* skip newline character, if any */
- if (*end == '\n')
- end++;
- if (*cp == 0 || *end != 0)
- return (-EINVAL);
- return (0);
-}
-
-static inline int
-kstrtol(const char *cp, unsigned int base, long *res)
-{
- char *end;
-
- *res = strtol(cp, &end, base);
-
- /* skip newline character, if any */
- if (*end == '\n')
- end++;
- if (*cp == 0 || *end != 0)
- return (-EINVAL);
- return (0);
-}
-
-static inline int
-kstrtoint(const char *cp, unsigned int base, int *res)
-{
- char *end;
- long temp;
-
- *res = temp = strtol(cp, &end, base);
-
- /* skip newline character, if any */
- if (*end == '\n')
- end++;
- if (*cp == 0 || *end != 0)
- return (-EINVAL);
- if (temp != (int)temp)
- return (-ERANGE);
- return (0);
-}
-
-static inline int
-kstrtouint(const char *cp, unsigned int base, unsigned int *res)
-{
- char *end;
- unsigned long temp;
-
- *res = temp = strtoul(cp, &end, base);
-
- /* skip newline character, if any */
- if (*end == '\n')
- end++;
- if (*cp == 0 || *end != 0)
- return (-EINVAL);
- if (temp != (unsigned int)temp)
- return (-ERANGE);
- return (0);
-}
-
-static inline int
-kstrtou8(const char *cp, unsigned int base, u8 *res)
-{
- char *end;
- unsigned long temp;
-
- *res = temp = strtoul(cp, &end, base);
-
- /* skip newline character, if any */
- if (*end == '\n')
- end++;
- if (*cp == 0 || *end != 0)
- return (-EINVAL);
- if (temp != (u8)temp)
- return (-ERANGE);
- return (0);
-}
-
-static inline int
-kstrtou16(const char *cp, unsigned int base, u16 *res)
-{
- char *end;
- unsigned long temp;
-
- *res = temp = strtoul(cp, &end, base);
-
- /* skip newline character, if any */
- if (*end == '\n')
- end++;
- if (*cp == 0 || *end != 0)
- return (-EINVAL);
- if (temp != (u16)temp)
- return (-ERANGE);
- return (0);
-}
-
-static inline int
-kstrtou32(const char *cp, unsigned int base, u32 *res)
-{
-
- return (kstrtouint(cp, base, res));
-}
-
-static inline int
-kstrtou64(const char *cp, unsigned int base, u64 *res)
-{
- char *end;
-
- *res = strtouq(cp, &end, base);
-
- /* skip newline character, if any */
- if (*end == '\n')
- end++;
- if (*cp == 0 || *end != 0)
- return (-EINVAL);
- return (0);
-}
-
-static inline int
-kstrtoull(const char *cp, unsigned int base, unsigned long long *res)
-{
- return (kstrtou64(cp, base, (u64 *)res));
-}
-
-static inline int
-kstrtobool(const char *s, bool *res)
-{
- int len;
-
- if (s == NULL || (len = strlen(s)) == 0 || res == NULL)
- return (-EINVAL);
-
- /* skip newline character, if any */
- if (s[len - 1] == '\n')
- len--;
-
- if (len == 1 && strchr("yY1", s[0]) != NULL)
- *res = true;
- else if (len == 1 && strchr("nN0", s[0]) != NULL)
- *res = false;
- else if (strncasecmp("on", s, len) == 0)
- *res = true;
- else if (strncasecmp("off", s, len) == 0)
- *res = false;
- else
- return (-EINVAL);
-
- return (0);
-}
-
-static inline int
-kstrtobool_from_user(const char __user *s, size_t count, bool *res)
-{
- char buf[8] = {};
-
- if (count > (sizeof(buf) - 1))
- count = (sizeof(buf) - 1);
-
- if (copy_from_user(buf, s, count))
- return (-EFAULT);
-
- return (kstrtobool(buf, res));
-}
-
-static inline int
-kstrtoint_from_user(const char __user *s, size_t count, unsigned int base,
- int *p)
-{
- char buf[36] = {};
-
- if (count > (sizeof(buf) - 1))
- count = (sizeof(buf) - 1);
-
- if (copy_from_user(buf, s, count))
- return (-EFAULT);
-
- return (kstrtoint(buf, base, p));
-}
-
-static inline int
-kstrtouint_from_user(const char __user *s, size_t count, unsigned int base,
- unsigned int *p)
-{
- char buf[36] = {};
-
- if (count > (sizeof(buf) - 1))
- count = (sizeof(buf) - 1);
-
- if (copy_from_user(buf, s, count))
- return (-EFAULT);
-
- return (kstrtouint(buf, base, p));
-}
-
-static inline int
-kstrtou32_from_user(const char __user *s, size_t count, unsigned int base,
- unsigned int *p)
-{
-
- return (kstrtouint_from_user(s, count, base, p));
-}
-
-static inline int
-kstrtou8_from_user(const char __user *s, size_t count, unsigned int base,
- u8 *p)
-{
- char buf[8] = {};
-
- if (count > (sizeof(buf) - 1))
- count = (sizeof(buf) - 1);
-
- if (copy_from_user(buf, s, count))
- return (-EFAULT);
-
- return (kstrtou8(buf, base, p));
-}
-
-#define min(x, y) ((x) < (y) ? (x) : (y))
-#define max(x, y) ((x) > (y) ? (x) : (y))
-
-#define min3(a, b, c) min(a, min(b,c))
-#define max3(a, b, c) max(a, max(b,c))
-
-#define min_t(type, x, y) ({ \
- type __min1 = (x); \
- type __min2 = (y); \
- __min1 < __min2 ? __min1 : __min2; })
-
-#define max_t(type, x, y) ({ \
- type __max1 = (x); \
- type __max2 = (y); \
- __max1 > __max2 ? __max1 : __max2; })
-
#define offsetofend(t, m) \
(offsetof(t, m) + sizeof((((t *)0)->m)))
-#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)
-
-/*
- * This looks more complex than it should be. But we need to
- * get the type for the ~ right in round_down (it needs to be
- * as wide as the result!), and we want to evaluate the macro
- * arguments just once each.
- */
-#define __round_mask(x, y) ((__typeof__(x))((y)-1))
-#define round_up(x, y) ((((x)-1) | __round_mask(x, y))+1)
-#define round_down(x, y) ((x) & ~__round_mask(x, y))
-
#define smp_processor_id() PCPU_GET(cpuid)
#define num_possible_cpus() mp_ncpus
#define num_online_cpus() mp_ncpus
@@ -559,31 +281,6 @@ extern bool linux_cpu_has_clflush;
#define cpu_has_clflush linux_cpu_has_clflush
#endif
-/* Swap values of a and b */
-#define swap(a, b) do { \
- typeof(a) _swap_tmp = a; \
- a = b; \
- b = _swap_tmp; \
-} while (0)
-
-#define DIV_ROUND_CLOSEST(x, divisor) (((x) + ((divisor) / 2)) / (divisor))
-
-#define DIV_ROUND_CLOSEST_ULL(x, divisor) ({ \
- __typeof(divisor) __d = (divisor); \
- unsigned long long __ret = (x) + (__d) / 2; \
- __ret /= __d; \
- __ret; \
-})
-
-static inline uintmax_t
-mult_frac(uintmax_t x, uintmax_t multiplier, uintmax_t divisor)
-{
- uintmax_t q = (x / divisor);
- uintmax_t r = (x % divisor);
-
- return ((q * multiplier) + ((r * multiplier) / divisor));
-}
-
typedef struct linux_ratelimit {
struct timeval lasttime;
int counter;
@@ -644,7 +341,7 @@ static inline bool
mac_pton(const char *macin, uint8_t *macout)
{
const char *s, *d;
- uint8_t mac[6], hx, lx;;
+ uint8_t mac[6], hx, lx;
int i;
if (strlen(macin) < (3 * 6 - 1))
diff --git a/sys/compat/linuxkpi/common/include/linux/kernel_stat.h b/sys/compat/linuxkpi/common/include/linux/kernel_stat.h
new file mode 100644
index 000000000000..c960b4ad2cff
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/kernel_stat.h
@@ -0,0 +1,34 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 Jean-Sébastien Pédron <dumbbell@FreeBSD.org>
+ *
+ * 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_KERNEL_STAT_H_
+#define _LINUXKPI_LINUX_KERNEL_STAT_H_
+
+#include <linux/interrupt.h>
+
+#endif /* _LINUXKPI_LINUX_KERNEL_STAT_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/kmod.h b/sys/compat/linuxkpi/common/include/linux/kmod.h
index b3cbe2ed2e02..8f9f034aabd8 100644
--- a/sys/compat/linuxkpi/common/include/linux/kmod.h
+++ b/sys/compat/linuxkpi/common/include/linux/kmod.h
@@ -33,7 +33,7 @@
#include <sys/syscallsubr.h>
#include <sys/refcount.h>
#include <sys/sbuf.h>
-#include <machine/stdarg.h>
+#include <sys/stdarg.h>
#include <sys/proc.h>
#define request_module(...) \
diff --git a/sys/compat/linuxkpi/common/include/linux/kobject.h b/sys/compat/linuxkpi/common/include/linux/kobject.h
index 512f47f9e4b4..98f55d1234c4 100644
--- a/sys/compat/linuxkpi/common/include/linux/kobject.h
+++ b/sys/compat/linuxkpi/common/include/linux/kobject.h
@@ -29,7 +29,7 @@
#ifndef _LINUXKPI_LINUX_KOBJECT_H_
#define _LINUXKPI_LINUX_KOBJECT_H_
-#include <machine/stdarg.h>
+#include <sys/stdarg.h>
#include <linux/kernel.h>
#include <linux/kref.h>
diff --git a/sys/compat/linuxkpi/common/include/linux/kref.h b/sys/compat/linuxkpi/common/include/linux/kref.h
index 47d61c9ee316..b2fba468f7df 100644
--- a/sys/compat/linuxkpi/common/include/linux/kref.h
+++ b/sys/compat/linuxkpi/common/include/linux/kref.h
@@ -41,8 +41,7 @@
#include <asm/atomic.h>
struct kref {
- /* XXX In Linux this is a refcount_t */
- atomic_t refcount;
+ refcount_t refcount;
};
static inline void
diff --git a/sys/compat/linuxkpi/common/include/linux/kstrtox.h b/sys/compat/linuxkpi/common/include/linux/kstrtox.h
new file mode 100644
index 000000000000..5da99de24197
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/kstrtox.h
@@ -0,0 +1,324 @@
+/*-
+ * Copyright (c) 2010 Isilon Systems, Inc.
+ * Copyright (c) 2010 iX Systems, Inc.
+ * Copyright (c) 2010 Panasas, Inc.
+ * Copyright (c) 2017-2018 Mellanox Technologies, Ltd.
+ * All rights reserved.
+ * Copyright (c) 2018 Johannes Lundberg <johalun0@gmail.com>
+ * Copyright (c) 2020-2022 The FreeBSD Foundation
+ * Copyright (c) 2021 Vladimir Kondratyev <wulf@FreeBSD.org>
+ * Copyright (c) 2023 Serenity Cyber Security, LLC
+ *
+ * Portions of this software were developed by Bjoern A. Zeeb and
+ * Emmanuel Vadot 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 unmodified, 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 ``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 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_KSTRTOX_H_
+#define _LINUXKPI_LINUX_KSTRTOX_H_
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/libkern.h>
+
+#include <linux/compiler.h>
+#include <linux/types.h>
+
+#include <asm/uaccess.h>
+
+static inline unsigned long long
+simple_strtoull(const char *cp, char **endp, unsigned int base)
+{
+ return (strtouq(cp, endp, base));
+}
+
+static inline long long
+simple_strtoll(const char *cp, char **endp, unsigned int base)
+{
+ return (strtoq(cp, endp, base));
+}
+
+static inline unsigned long
+simple_strtoul(const char *cp, char **endp, unsigned int base)
+{
+ return (strtoul(cp, endp, base));
+}
+
+static inline long
+simple_strtol(const char *cp, char **endp, unsigned int base)
+{
+ return (strtol(cp, endp, base));
+}
+
+static inline int
+kstrtoul(const char *cp, unsigned int base, unsigned long *res)
+{
+ char *end;
+
+ *res = strtoul(cp, &end, base);
+
+ /* skip newline character, if any */
+ if (*end == '\n')
+ end++;
+ if (*cp == 0 || *end != 0)
+ return (-EINVAL);
+ return (0);
+}
+
+static inline int
+kstrtol(const char *cp, unsigned int base, long *res)
+{
+ char *end;
+
+ *res = strtol(cp, &end, base);
+
+ /* skip newline character, if any */
+ if (*end == '\n')
+ end++;
+ if (*cp == 0 || *end != 0)
+ return (-EINVAL);
+ return (0);
+}
+
+static inline int
+kstrtoint(const char *cp, unsigned int base, int *res)
+{
+ char *end;
+ long temp;
+
+ *res = temp = strtol(cp, &end, base);
+
+ /* skip newline character, if any */
+ if (*end == '\n')
+ end++;
+ if (*cp == 0 || *end != 0)
+ return (-EINVAL);
+ if (temp != (int)temp)
+ return (-ERANGE);
+ return (0);
+}
+
+static inline int
+kstrtouint(const char *cp, unsigned int base, unsigned int *res)
+{
+ char *end;
+ unsigned long temp;
+
+ *res = temp = strtoul(cp, &end, base);
+
+ /* skip newline character, if any */
+ if (*end == '\n')
+ end++;
+ if (*cp == 0 || *end != 0)
+ return (-EINVAL);
+ if (temp != (unsigned int)temp)
+ return (-ERANGE);
+ return (0);
+}
+
+static inline int
+kstrtou8(const char *cp, unsigned int base, uint8_t *res)
+{
+ char *end;
+ unsigned long temp;
+
+ *res = temp = strtoul(cp, &end, base);
+
+ /* skip newline character, if any */
+ if (*end == '\n')
+ end++;
+ if (*cp == 0 || *end != 0)
+ return (-EINVAL);
+ if (temp != (uint8_t)temp)
+ return (-ERANGE);
+ return (0);
+}
+
+static inline int
+kstrtou16(const char *cp, unsigned int base, uint16_t *res)
+{
+ char *end;
+ unsigned long temp;
+
+ *res = temp = strtoul(cp, &end, base);
+
+ /* skip newline character, if any */
+ if (*end == '\n')
+ end++;
+ if (*cp == 0 || *end != 0)
+ return (-EINVAL);
+ if (temp != (uint16_t)temp)
+ return (-ERANGE);
+ return (0);
+}
+
+static inline int
+kstrtou32(const char *cp, unsigned int base, uint32_t *res)
+{
+
+ return (kstrtouint(cp, base, res));
+}
+
+static inline int
+kstrtos32(const char *cp, unsigned int base, int32_t *res)
+{
+
+ return (kstrtoint(cp, base, res));
+}
+
+static inline int
+kstrtos64(const char *cp, unsigned int base, int64_t *res)
+{
+ char *end;
+
+ *res = strtoq(cp, &end, base);
+
+ /* skip newline character, if any */
+ if (*end == '\n')
+ end++;
+ if (*cp == 0 || *end != 0)
+ return (-EINVAL);
+ return (0);
+}
+
+static inline int
+kstrtoll(const char *cp, unsigned int base, long long *res)
+{
+ return (kstrtos64(cp, base, (int64_t *)res));
+}
+
+static inline int
+kstrtou64(const char *cp, unsigned int base, u64 *res)
+{
+ char *end;
+
+ *res = strtouq(cp, &end, base);
+
+ /* skip newline character, if any */
+ if (*end == '\n')
+ end++;
+ if (*cp == 0 || *end != 0)
+ return (-EINVAL);
+ return (0);
+}
+
+static inline int
+kstrtoull(const char *cp, unsigned int base, unsigned long long *res)
+{
+ return (kstrtou64(cp, base, (uint64_t *)res));
+}
+
+static inline int
+kstrtobool(const char *s, bool *res)
+{
+ int len;
+
+ if (s == NULL || (len = strlen(s)) == 0 || res == NULL)
+ return (-EINVAL);
+
+ /* skip newline character, if any */
+ if (s[len - 1] == '\n')
+ len--;
+
+ if (len == 1 && strchr("yY1", s[0]) != NULL)
+ *res = true;
+ else if (len == 1 && strchr("nN0", s[0]) != NULL)
+ *res = false;
+ else if (strncasecmp("on", s, len) == 0)
+ *res = true;
+ else if (strncasecmp("off", s, len) == 0)
+ *res = false;
+ else
+ return (-EINVAL);
+
+ return (0);
+}
+
+static inline int
+kstrtobool_from_user(const char __user *s, size_t count, bool *res)
+{
+ char buf[8] = {};
+
+ if (count > (sizeof(buf) - 1))
+ count = (sizeof(buf) - 1);
+
+ if (copy_from_user(buf, s, count))
+ return (-EFAULT);
+
+ return (kstrtobool(buf, res));
+}
+
+static inline int
+kstrtoint_from_user(const char __user *s, size_t count, unsigned int base,
+ int *p)
+{
+ char buf[36] = {};
+
+ if (count > (sizeof(buf) - 1))
+ count = (sizeof(buf) - 1);
+
+ if (copy_from_user(buf, s, count))
+ return (-EFAULT);
+
+ return (kstrtoint(buf, base, p));
+}
+
+static inline int
+kstrtouint_from_user(const char __user *s, size_t count, unsigned int base,
+ unsigned int *p)
+{
+ char buf[36] = {};
+
+ if (count > (sizeof(buf) - 1))
+ count = (sizeof(buf) - 1);
+
+ if (copy_from_user(buf, s, count))
+ return (-EFAULT);
+
+ return (kstrtouint(buf, base, p));
+}
+
+static inline int
+kstrtou32_from_user(const char __user *s, size_t count, unsigned int base,
+ unsigned int *p)
+{
+
+ return (kstrtouint_from_user(s, count, base, p));
+}
+
+static inline int
+kstrtou8_from_user(const char __user *s, size_t count, unsigned int base,
+ uint8_t *p)
+{
+ char buf[8] = {};
+
+ if (count > (sizeof(buf) - 1))
+ count = (sizeof(buf) - 1);
+
+ if (copy_from_user(buf, s, count))
+ return (-EFAULT);
+
+ return (kstrtou8(buf, base, p));
+}
+
+#endif /* _LINUXKPI_LINUX_KSTRTOX_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/ktime.h b/sys/compat/linuxkpi/common/include/linux/ktime.h
index 53c2abd64fc6..6a2f04f3d789 100644
--- a/sys/compat/linuxkpi/common/include/linux/ktime.h
+++ b/sys/compat/linuxkpi/common/include/linux/ktime.h
@@ -232,6 +232,13 @@ ktime_get_boottime_ns(void)
return (ktime_to_ns(ktime_get_boottime()));
}
+static inline uint64_t
+ktime_get_boottime_seconds(void)
+{
+
+ return (ktime_divns(ktime_get_boottime(), NSEC_PER_SEC));
+}
+
static inline ktime_t
ktime_get_real(void)
{
diff --git a/sys/compat/linuxkpi/common/include/linux/leds.h b/sys/compat/linuxkpi/common/include/linux/leds.h
index f7ee7a68dcf5..89f7286f6800 100644
--- a/sys/compat/linuxkpi/common/include/linux/leds.h
+++ b/sys/compat/linuxkpi/common/include/linux/leds.h
@@ -27,7 +27,7 @@
#define _LINUXKPI_LINUX_LEDS_H
enum led_brightness {
- __DUMMY,
+ LED_OFF,
};
struct led_classdev {
@@ -35,6 +35,7 @@ struct led_classdev {
const char *default_trigger;
int (*blink_set)(struct led_classdev *, unsigned long *, unsigned long *);
void (*brightness_set)(struct led_classdev *, enum led_brightness);
+ void (*led_set)(struct led_classdev *, enum led_brightness);
};
#endif /* _LINUXKPI_LINUX_LEDS_H */
diff --git a/sys/compat/linuxkpi/common/include/linux/list.h b/sys/compat/linuxkpi/common/include/linux/list.h
index eecb517d780e..a6c74a324dac 100644
--- a/sys/compat/linuxkpi/common/include/linux/list.h
+++ b/sys/compat/linuxkpi/common/include/linux/list.h
@@ -234,6 +234,11 @@ list_del_init(struct list_head *entry)
for (; &p->field != (h); \
p = list_prev_entry(p, field))
+#define list_for_each_rcu(p, head) \
+ for (p = rcu_dereference((head)->next); \
+ p != (head); \
+ p = rcu_dereference((p)->next))
+
static inline void
list_add(struct list_head *new, struct list_head *head)
{
diff --git a/sys/compat/linuxkpi/common/include/linux/lockdep.h b/sys/compat/linuxkpi/common/include/linux/lockdep.h
index a379e191aad7..93fe445f7057 100644
--- a/sys/compat/linuxkpi/common/include/linux/lockdep.h
+++ b/sys/compat/linuxkpi/common/include/linux/lockdep.h
@@ -30,6 +30,7 @@
#ifndef _LINUXKPI_LINUX_LOCKDEP_H_
#define _LINUXKPI_LINUX_LOCKDEP_H_
+#include <sys/systm.h>
#include <sys/types.h>
#include <sys/lock.h>
@@ -50,8 +51,8 @@ struct pin_cookie {
#define lockdep_unregister_key(key) do { } while(0)
#ifdef INVARIANTS
-#define lockdep_assert(cond) do { WARN_ON(!cond); } while (0)
-#define lockdep_assert_once(cond) do { WARN_ON_ONCE(!cond); } while (0)
+#define lockdep_assert(cond) do { WARN_ON(!(cond)); } while (0)
+#define lockdep_assert_once(cond) do { WARN_ON_ONCE(!(cond)); } while (0)
#define lockdep_assert_not_held(m) do { \
struct lock_object *__lock = (struct lock_object *)(m); \
@@ -70,17 +71,6 @@ struct pin_cookie {
#define lockdep_assert_none_held_once() do { } while (0)
-static __inline bool
-lockdep_is_held(void *__m)
-{
- struct lock_object *__lock;
- struct thread *__td;
-
- __lock = __m;
- return (LOCK_CLASS(__lock)->lc_owner(__lock, &__td) != 0);
-}
-#define lockdep_is_held_type(_m, _t) lockdep_is_held(_m)
-
#else
#define lockdep_assert(cond) do { } while (0)
#define lockdep_assert_once(cond) do { } while (0)
@@ -91,10 +81,23 @@ lockdep_is_held(void *__m)
#define lockdep_assert_held_once(m) do { (void)(m); } while (0)
-#define lockdep_is_held(m) 1
-#define lockdep_is_held_type(_m, _t) 1
#endif
+static __inline bool
+lockdep_is_held(void *__m __diagused)
+{
+#ifdef INVARIANTS
+ struct lock_object *__lock;
+ struct thread *__td;
+
+ __lock = __m;
+ return (LOCK_CLASS(__lock)->lc_owner(__lock, &__td) != 0);
+#else
+ return (true);
+#endif
+}
+#define lockdep_is_held_type(_m, _t) lockdep_is_held(_m)
+
#define might_lock(m) do { } while (0)
#define might_lock_read(m) do { } while (0)
#define might_lock_nested(m, n) do { } while (0)
diff --git a/sys/compat/linuxkpi/common/include/linux/log2.h b/sys/compat/linuxkpi/common/include/linux/log2.h
index 27e91a8bdbe0..660e9adb6fa9 100644
--- a/sys/compat/linuxkpi/common/include/linux/log2.h
+++ b/sys/compat/linuxkpi/common/include/linux/log2.h
@@ -33,97 +33,9 @@
#include <sys/libkern.h>
-static inline unsigned long
-roundup_pow_of_two(unsigned long x)
-{
- return (1UL << flsl(x - 1));
-}
-
-static inline int
-is_power_of_2(unsigned long n)
-{
- return (n == roundup_pow_of_two(n));
-}
-
-static inline unsigned long
-rounddown_pow_of_two(unsigned long x)
-{
- return (1UL << (flsl(x) - 1));
-}
-
-#define ilog2(n) \
-( \
- __builtin_constant_p(n) ? ( \
- (n) < 1 ? -1 : \
- (n) & (1ULL << 63) ? 63 : \
- (n) & (1ULL << 62) ? 62 : \
- (n) & (1ULL << 61) ? 61 : \
- (n) & (1ULL << 60) ? 60 : \
- (n) & (1ULL << 59) ? 59 : \
- (n) & (1ULL << 58) ? 58 : \
- (n) & (1ULL << 57) ? 57 : \
- (n) & (1ULL << 56) ? 56 : \
- (n) & (1ULL << 55) ? 55 : \
- (n) & (1ULL << 54) ? 54 : \
- (n) & (1ULL << 53) ? 53 : \
- (n) & (1ULL << 52) ? 52 : \
- (n) & (1ULL << 51) ? 51 : \
- (n) & (1ULL << 50) ? 50 : \
- (n) & (1ULL << 49) ? 49 : \
- (n) & (1ULL << 48) ? 48 : \
- (n) & (1ULL << 47) ? 47 : \
- (n) & (1ULL << 46) ? 46 : \
- (n) & (1ULL << 45) ? 45 : \
- (n) & (1ULL << 44) ? 44 : \
- (n) & (1ULL << 43) ? 43 : \
- (n) & (1ULL << 42) ? 42 : \
- (n) & (1ULL << 41) ? 41 : \
- (n) & (1ULL << 40) ? 40 : \
- (n) & (1ULL << 39) ? 39 : \
- (n) & (1ULL << 38) ? 38 : \
- (n) & (1ULL << 37) ? 37 : \
- (n) & (1ULL << 36) ? 36 : \
- (n) & (1ULL << 35) ? 35 : \
- (n) & (1ULL << 34) ? 34 : \
- (n) & (1ULL << 33) ? 33 : \
- (n) & (1ULL << 32) ? 32 : \
- (n) & (1ULL << 31) ? 31 : \
- (n) & (1ULL << 30) ? 30 : \
- (n) & (1ULL << 29) ? 29 : \
- (n) & (1ULL << 28) ? 28 : \
- (n) & (1ULL << 27) ? 27 : \
- (n) & (1ULL << 26) ? 26 : \
- (n) & (1ULL << 25) ? 25 : \
- (n) & (1ULL << 24) ? 24 : \
- (n) & (1ULL << 23) ? 23 : \
- (n) & (1ULL << 22) ? 22 : \
- (n) & (1ULL << 21) ? 21 : \
- (n) & (1ULL << 20) ? 20 : \
- (n) & (1ULL << 19) ? 19 : \
- (n) & (1ULL << 18) ? 18 : \
- (n) & (1ULL << 17) ? 17 : \
- (n) & (1ULL << 16) ? 16 : \
- (n) & (1ULL << 15) ? 15 : \
- (n) & (1ULL << 14) ? 14 : \
- (n) & (1ULL << 13) ? 13 : \
- (n) & (1ULL << 12) ? 12 : \
- (n) & (1ULL << 11) ? 11 : \
- (n) & (1ULL << 10) ? 10 : \
- (n) & (1ULL << 9) ? 9 : \
- (n) & (1ULL << 8) ? 8 : \
- (n) & (1ULL << 7) ? 7 : \
- (n) & (1ULL << 6) ? 6 : \
- (n) & (1ULL << 5) ? 5 : \
- (n) & (1ULL << 4) ? 4 : \
- (n) & (1ULL << 3) ? 3 : \
- (n) & (1ULL << 2) ? 2 : \
- (n) & (1ULL << 1) ? 1 : \
- (n) & (1ULL << 0) ? 0 : \
- -1) : \
- (sizeof(n) <= 4) ? \
- fls((u32)(n)) - 1 : flsll((u64)(n)) - 1 \
-)
-
-#define order_base_2(x) ilog2(roundup_pow_of_two(x))
+#define is_power_of_2(n) ({ \
+ __typeof(n) _n = (n); \
+ _n != 0 && (_n & (_n - 1)) == 0; \
+})
#endif /* _LINUXKPI_LINUX_LOG2_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/math.h b/sys/compat/linuxkpi/common/include/linux/math.h
new file mode 100644
index 000000000000..5a348a57747b
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/math.h
@@ -0,0 +1,76 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2013-2015 Mellanox Technologies, Ltd.
+ * Copyright (c) 2014-2015 François Tigeot
+ * Copyright (c) 2016 Matt Macy <mmacy@FreeBSD.org>
+ * Copyright (c) 2019 Johannes Lundberg <johalun@FreeBSD.org>
+ * Copyright (c) 2023 Serenity Cyber Security, LLC.
+ *
+ * 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_MATH_H_
+#define _LINUXKPI_LINUX_MATH_H_
+
+#include <linux/types.h>
+
+/*
+ * This looks more complex than it should be. But we need to
+ * get the type for the ~ right in round_down (it needs to be
+ * as wide as the result!), and we want to evaluate the macro
+ * arguments just once each.
+ */
+#define __round_mask(x, y) ((__typeof__(x))((y)-1))
+#define round_up(x, y) ((((x)-1) | __round_mask(x, y))+1)
+#define round_down(x, y) ((x) & ~__round_mask(x, y))
+
+#define DIV_ROUND_UP(x, n) howmany(x, n)
+#define DIV_ROUND_UP_ULL(x, n) DIV_ROUND_UP((unsigned long long)(x), (n))
+#define DIV_ROUND_DOWN_ULL(x, n) ((unsigned long long)(x) / (n))
+
+#define DIV_ROUND_CLOSEST(x, divisor) (((x) + ((divisor) / 2)) / (divisor))
+#define DIV_ROUND_CLOSEST_ULL(x, divisor) ({ \
+ __typeof(divisor) __d = (divisor); \
+ unsigned long long __ret = (x) + (__d) / 2; \
+ __ret /= __d; \
+ __ret; \
+})
+
+#if defined(LINUXKPI_VERSION) && LINUXKPI_VERSION >= 60600
+#define abs_diff(x, y) ({ \
+ __typeof(x) _x = (x); \
+ __typeof(y) _y = (y); \
+ _x > _y ? _x - _y : _y - _x; \
+})
+#endif
+
+static inline uintmax_t
+mult_frac(uintmax_t x, uintmax_t multiplier, uintmax_t divisor)
+{
+ uintmax_t q = (x / divisor);
+ uintmax_t r = (x % divisor);
+
+ return ((q * multiplier) + ((r * multiplier) / divisor));
+}
+
+#endif /* _LINUXKPI_LINUX_MATH_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/math64.h b/sys/compat/linuxkpi/common/include/linux/math64.h
index cae5e30b08df..a216d350570f 100644
--- a/sys/compat/linuxkpi/common/include/linux/math64.h
+++ b/sys/compat/linuxkpi/common/include/linux/math64.h
@@ -61,6 +61,8 @@ div64_u64(uint64_t dividend, uint64_t divisor)
return (dividend / divisor);
}
+#define div64_ul(x, y) div64_u64((x), (y))
+
static inline uint64_t
div_u64_rem(uint64_t dividend, uint32_t divisor, uint32_t *remainder)
{
diff --git a/sys/compat/linuxkpi/common/include/linux/mhi.h b/sys/compat/linuxkpi/common/include/linux/mhi.h
index 2e0b74f29def..24b3205d6f5a 100644
--- a/sys/compat/linuxkpi/common/include/linux/mhi.h
+++ b/sys/compat/linuxkpi/common/include/linux/mhi.h
@@ -75,6 +75,8 @@ struct mhi_controller {
void *regs;
int *irq;
const char *fw_image;
+ const u8 *fw_data;
+ size_t fw_sz;
bool fbc_download;
size_t rddm_size;
diff --git a/sys/compat/linuxkpi/common/include/linux/minmax.h b/sys/compat/linuxkpi/common/include/linux/minmax.h
new file mode 100644
index 000000000000..d48958f0899f
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/minmax.h
@@ -0,0 +1,74 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2010 iX Systems, Inc.
+ * Copyright (c) 2010 Panasas, Inc.
+ * Copyright (c) 2013-2015 Mellanox Technologies, Ltd.
+ * Copyright (c) 2014-2015 François Tigeot
+ * Copyright (c) 2015 Hans Petter Selasky <hselasky@FreeBSD.org>
+ * Copyright (c) 2016 Matt Macy <mmacy@FreeBSD.org>
+ * Copyright (c) 2023 Serenity Cyber Security, LLC.
+ *
+ * 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_MINMAX_H_
+#define _LINUXKPI_LINUX_MINMAX_H_
+
+#include <linux/build_bug.h>
+#include <linux/compiler.h>
+#include <linux/types.h>
+
+#define min(x, y) ((x) < (y) ? (x) : (y))
+#define max(x, y) ((x) > (y) ? (x) : (y))
+
+#define min3(a, b, c) min(a, min(b, c))
+#define max3(a, b, c) max(a, max(b, c))
+
+#define min_not_zero(x, y) ({ \
+ __typeof(x) __min1 = (x); \
+ __typeof(y) __min2 = (y); \
+ __min1 == 0 ? __min2 : ((__min2 == 0) ? __min1 : min(__min1, __min2));\
+})
+
+#define min_t(type, x, y) ({ \
+ type __min1 = (x); \
+ type __min2 = (y); \
+ __min1 < __min2 ? __min1 : __min2; })
+
+#define max_t(type, x, y) ({ \
+ type __max1 = (x); \
+ type __max2 = (y); \
+ __max1 > __max2 ? __max1 : __max2; })
+
+#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)
+
+/* Swap values of a and b */
+#define swap(a, b) do { \
+ __typeof(a) _swap_tmp = a; \
+ a = b; \
+ b = _swap_tmp; \
+} while (0)
+
+#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 109bfffe7d6a..156b00a0c0f0 100644
--- a/sys/compat/linuxkpi/common/include/linux/mm.h
+++ b/sys/compat/linuxkpi/common/include/linux/mm.h
@@ -35,6 +35,7 @@
#include <linux/gfp.h>
#include <linux/kernel.h>
#include <linux/mm_types.h>
+#include <linux/mmzone.h>
#include <linux/pfn.h>
#include <linux/list.h>
#include <linux/mmap_lock.h>
@@ -160,6 +161,14 @@ virt_to_head_page(const void *p)
return (virt_to_page(p));
}
+static inline struct folio *
+virt_to_folio(const void *p)
+{
+ struct page *page = virt_to_page(p);
+
+ return (page_folio(page));
+}
+
/*
* Compute log2 of the power of two rounded up count of pages
* needed for size bytes.
@@ -178,6 +187,14 @@ get_order(unsigned long size)
return (order);
}
+/*
+ * Resolve a page into a virtual address:
+ *
+ * NOTE: This function only works for pages allocated by the kernel.
+ */
+void *linux_page_address(const struct page *);
+#define page_address(page) linux_page_address(page)
+
static inline void *
lowmem_page_address(struct page *page)
{
@@ -266,18 +283,65 @@ get_page(struct page *page)
vm_page_wire(page);
}
-extern long
-get_user_pages(unsigned long start, unsigned long nr_pages,
- unsigned int gup_flags, struct page **,
- struct vm_area_struct **);
+static inline void
+put_page(struct page *page)
+{
+ /* `__free_page()` takes care of the refcounting (unwire). */
+ __free_page(page);
+}
+static inline void
+folio_get(struct folio *folio)
+{
+ get_page(&folio->page);
+}
+
+static inline void
+folio_put(struct folio *folio)
+{
+ put_page(&folio->page);
+}
+
+/*
+ * Linux uses the following "transparent" union so that `release_pages()`
+ * accepts both a list of `struct page` or a list of `struct folio`. This
+ * relies on the fact that a `struct folio` can be cast to a `struct page`.
+ */
+typedef union {
+ struct page **pages;
+ struct folio **folios;
+} release_pages_arg __attribute__ ((__transparent_union__));
+
+void linux_release_pages(release_pages_arg arg, int nr);
+#define release_pages(arg, nr) linux_release_pages((arg), (nr))
+
+extern long
+lkpi_get_user_pages(unsigned long start, unsigned long nr_pages,
+ unsigned int gup_flags, struct page **);
+#if defined(LINUXKPI_VERSION) && LINUXKPI_VERSION >= 60500
+#define get_user_pages(start, nr_pages, gup_flags, pages) \
+ lkpi_get_user_pages(start, nr_pages, gup_flags, pages)
+#else
+#define get_user_pages(start, nr_pages, gup_flags, pages, vmas) \
+ lkpi_get_user_pages(start, nr_pages, gup_flags, pages)
+#endif
+
+#if defined(LINUXKPI_VERSION) && LINUXKPI_VERSION >= 60500
+static inline long
+pin_user_pages(unsigned long start, unsigned long nr_pages,
+ unsigned int gup_flags, struct page **pages)
+{
+ return (get_user_pages(start, nr_pages, gup_flags, pages));
+}
+#else
static inline long
pin_user_pages(unsigned long start, unsigned long nr_pages,
unsigned int gup_flags, struct page **pages,
struct vm_area_struct **vmas)
{
- return get_user_pages(start, nr_pages, gup_flags, pages, vmas);
+ return (get_user_pages(start, nr_pages, gup_flags, pages, vmas));
}
+#endif
extern int
__get_user_pages_fast(unsigned long start, int nr_pages, int write,
@@ -307,12 +371,6 @@ pin_user_pages_remote(struct task_struct *task, struct mm_struct *mm,
task, mm, start, nr_pages, gup_flags, pages, vmas);
}
-static inline void
-put_page(struct page *page)
-{
- vm_page_unwire(page, PQ_ACTIVE);
-}
-
#define unpin_user_page(page) put_page(page)
#define unpin_user_pages(pages, npages) release_pages(pages, npages)
@@ -348,14 +406,14 @@ vmalloc_to_page(const void *addr)
static inline int
trylock_page(struct page *page)
{
- return (vm_page_trylock(page));
+ return (vm_page_tryxbusy(page));
}
static inline void
unlock_page(struct page *page)
{
- vm_page_unlock(page);
+ vm_page_xunbusy(page);
}
extern int is_vmalloc_addr(const void *addr);
@@ -388,4 +446,34 @@ want_init_on_free(void)
return (false);
}
+static inline unsigned long
+folio_pfn(struct folio *folio)
+{
+ return (page_to_pfn(&folio->page));
+}
+
+static inline long
+folio_nr_pages(struct folio *folio)
+{
+ return (1);
+}
+
+static inline size_t
+folio_size(struct folio *folio)
+{
+ return (PAGE_SIZE);
+}
+
+static inline void
+folio_mark_dirty(struct folio *folio)
+{
+ set_page_dirty(&folio->page);
+}
+
+static inline void *
+folio_address(const struct folio *folio)
+{
+ return (page_address(&folio->page));
+}
+
#endif /* _LINUXKPI_LINUX_MM_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/mm_types.h b/sys/compat/linuxkpi/common/include/linux/mm_types.h
index c08e2511725b..3ea68e97004c 100644
--- a/sys/compat/linuxkpi/common/include/linux/mm_types.h
+++ b/sys/compat/linuxkpi/common/include/linux/mm_types.h
@@ -79,4 +79,15 @@ mmgrab(struct mm_struct *mm)
extern struct mm_struct *linux_get_task_mm(struct task_struct *);
#define get_task_mm(task) linux_get_task_mm(task)
+struct folio {
+ /*
+ * The page member must be at the beginning because `page_folio(p)`
+ * casts from a `struct page` to a `struct folio`.
+ *
+ * `release_pages()` also relies on this to be able to accept either a
+ * list of `struct page` or a list of `struct folio`.
+ */
+ struct page page;
+};
+
#endif /* _LINUXKPI_LINUX_MM_TYPES_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/mmzone.h b/sys/compat/linuxkpi/common/include/linux/mmzone.h
index 49cc218c6fce..57d3dcac9597 100644
--- a/sys/compat/linuxkpi/common/include/linux/mmzone.h
+++ b/sys/compat/linuxkpi/common/include/linux/mmzone.h
@@ -4,8 +4,12 @@
#define _LINUX_MMZONE_H
#include <linux/mm_types.h>
+#include <linux/numa.h>
#include <linux/page-flags.h>
#define MAX_ORDER 11
+#define MAX_PAGE_ORDER 10
+#define NR_PAGE_ORDERS (MAX_PAGE_ORDER + 1)
+
#endif
diff --git a/sys/compat/linuxkpi/common/include/linux/module.h b/sys/compat/linuxkpi/common/include/linux/module.h
index 9d98ef7cf3db..079dacf8df6c 100644
--- a/sys/compat/linuxkpi/common/include/linux/module.h
+++ b/sys/compat/linuxkpi/common/include/linux/module.h
@@ -32,6 +32,8 @@
#include <sys/types.h>
#include <sys/param.h>
#include <sys/module.h>
+#include <sys/queue.h>
+#include <sys/linker.h>
#include <linux/list.h>
#include <linux/compiler.h>
@@ -51,7 +53,26 @@
#define MODULE_SUPPORTED_DEVICE(name)
#define MODULE_IMPORT_NS(_name)
+/*
+ * THIS_MODULE is used to differentiate modules on Linux. We currently
+ * completely stub out any Linux struct module usage, but THIS_MODULE is still
+ * used to populate the "owner" fields of various drivers. Even though we
+ * don't actually dereference these "owner" fields they are still used by
+ * drivers to check if devices/dmabufs/etc come from different modules. For
+ * example, during DRM GEM import some drivers check if the dmabuf's owner
+ * matches the dev's owner. If they match because they are both NULL drivers
+ * may incorrectly think two resources come from the same module.
+ *
+ * To handle this we specify an undefined symbol __this_linker_file, which
+ * will get special treatment from the linker when resolving. This will
+ * populate the usages of __this_linker_file with the linker_file_t of the
+ * module.
+ */
+#ifdef KLD_MODULE
+#define THIS_MODULE ((struct module *)&__this_linker_file)
+#else
#define THIS_MODULE ((struct module *)0)
+#endif
#define __MODULE_STRING(x) __stringify(x)
diff --git a/sys/compat/linuxkpi/common/include/linux/mutex.h b/sys/compat/linuxkpi/common/include/linux/mutex.h
index 1d85ba20baca..6fb6a7744a89 100644
--- a/sys/compat/linuxkpi/common/include/linux/mutex.h
+++ b/sys/compat/linuxkpi/common/include/linux/mutex.h
@@ -35,6 +35,7 @@
#include <sys/sx.h>
#include <linux/kernel.h>
+#include <linux/cleanup.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <asm/atomic.h>
diff --git a/sys/compat/linuxkpi/common/include/linux/netdev_features.h b/sys/compat/linuxkpi/common/include/linux/netdev_features.h
index 06e88d107708..fae82776b071 100644
--- a/sys/compat/linuxkpi/common/include/linux/netdev_features.h
+++ b/sys/compat/linuxkpi/common/include/linux/netdev_features.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2020-2021 The FreeBSD Foundation
+ * Copyright (c) 2020-2025 The FreeBSD Foundation
*
* Portions of this software were developed by Björn Zeeb
* under sponsorship from the FreeBSD Foundation.
@@ -33,15 +33,20 @@
typedef uint32_t netdev_features_t;
-#define NETIF_F_HIGHDMA BIT(0)
-#define NETIF_F_SG BIT(1)
-#define NETIF_F_IP_CSUM BIT(2)
-#define NETIF_F_IPV6_CSUM BIT(3)
-#define NETIF_F_TSO BIT(4)
-#define NETIF_F_TSO6 BIT(5)
-#define NETIF_F_RXCSUM BIT(6)
-#define NETIF_F_HW_CSUM BIT(7)
+#define NETIF_F_HIGHDMA BIT(0) /* Can DMA to high memory. */
+#define NETIF_F_SG BIT(1) /* Can do scatter/gather I/O. */
+#define NETIF_F_IP_CSUM BIT(2) /* Can csum TCP/UDP on IPv4. */
+#define NETIF_F_IPV6_CSUM BIT(3) /* Can csum TCP/UDP on IPv6. */
+#define NETIF_F_TSO BIT(4) /* Can do TCP over IPv4 segmentation. */
+#define NETIF_F_TSO6 BIT(5) /* Can do TCP over IPv6 segmentation. */
+#define NETIF_F_RXCSUM BIT(6) /* Can do receive csum offload. */
+#define NETIF_F_HW_CSUM BIT(7) /* Can csum packets (which?). */
+#define NETIF_F_HW_TC BIT(8) /* Can offload TC. */
-#define NETIF_F_CSUM_MASK (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)
+#define NETIF_F_CSUM_MASK (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)
+
+#define NETIF_F_BITS \
+ "\20\1HIGHDMA\2SG\3IP_CSUM\4IPV6_CSUM\5TSO\6TSO6\7RXCSUM" \
+ "\10HW_CSUM\11HW_TC"
#endif /* _LINUXKPI_LINUX_NETDEV_FEATURES_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/netdevice.h b/sys/compat/linuxkpi/common/include/linux/netdevice.h
index 3d2b309909b4..cd7d23077a62 100644
--- a/sys/compat/linuxkpi/common/include/linux/netdevice.h
+++ b/sys/compat/linuxkpi/common/include/linux/netdevice.h
@@ -75,6 +75,10 @@ struct wireless_dev; /* net/cfg80211.h */
#define NET_NAME_UNKNOWN 0
+enum net_addr_assign_type {
+ NET_ADDR_RANDOM,
+};
+
enum netdev_tx {
NETDEV_TX_OK = 0,
};
@@ -95,6 +99,10 @@ enum net_device_reg_state {
NETREG_REGISTERED,
};
+enum tc_setup_type {
+ TC_SETUP_MAX_DUMMY,
+};
+
struct net_device_ops {
int (*ndo_open)(struct net_device *);
int (*ndo_stop)(struct net_device *);
@@ -122,6 +130,7 @@ struct net_device {
unsigned long tx_errors;
unsigned long tx_packets;
} stats;
+ enum net_addr_assign_type addr_assign_type;
enum net_device_reg_state reg_state;
const struct ethtool_ops *ethtool_ops;
const struct net_device_ops *netdev_ops;
@@ -457,6 +466,8 @@ void linuxkpi_free_netdev(struct net_device *);
#define alloc_netdev(_l, _n, _f, _func) \
linuxkpi_alloc_netdev(_l, _n, _f, _func)
+#define alloc_netdev_dummy(_l) \
+ linuxkpi_alloc_netdev(_l, "dummy", NET_NAME_UNKNOWN, NULL)
#define free_netdev(_n) \
linuxkpi_free_netdev(_n)
diff --git a/sys/compat/linuxkpi/common/include/linux/nl80211.h b/sys/compat/linuxkpi/common/include/linux/nl80211.h
index 5b43ff675e19..f3979d3a2abc 100644
--- a/sys/compat/linuxkpi/common/include/linux/nl80211.h
+++ b/sys/compat/linuxkpi/common/include/linux/nl80211.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2020-2023 The FreeBSD Foundation
+ * Copyright (c) 2020-2024 The FreeBSD Foundation
*
* This software was developed by Björn Zeeb under sponsorship from
* the FreeBSD Foundation.
@@ -80,6 +80,11 @@ enum nl80211_reg_rule_flags {
NL80211_RRF_NO_OFDM = BIT(10),
NL80211_RRF_NO_320MHZ = BIT(11),
NL80211_RRF_NO_EHT = BIT(12),
+ NL80211_RRF_DFS_CONCURRENT = BIT(13),
+ NL80211_RRF_NO_6GHZ_VLP_CLIENT = BIT(14),
+ NL80211_RRF_NO_6GHZ_AFC_CLIENT = BIT(15),
+ NL80211_RRF_PSD = BIT(16),
+ NL80211_RRF_ALLOW_6GHZ_VLP_AP = BIT(17),
};
#define NL80211_RRF_NO_HT40 (NL80211_RRF_NO_HT40MINUS|NL80211_RRF_NO_HT40PLUS)
@@ -128,11 +133,13 @@ enum nl80211_band {
/* Keep this last. */
NUM_NL80211_BANDS
-};
+} __packed;
-enum nl80211_chan_flags {
- /* XXX TODO */
+enum nl80211_channel_type {
NL80211_CHAN_NO_HT,
+ NL80211_CHAN_HT20,
+ NL80211_CHAN_HT40PLUS,
+ NL80211_CHAN_HT40MINUS,
};
enum nl80211_chan_width {
@@ -183,8 +190,6 @@ enum nl80211_tdls_operation {
NL80211_TDLS_ENABLE_LINK,
NL80211_TDLS_DISABLE_LINK,
NL80211_TDLS_DISCOVERY_REQ,
- NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY,
- NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY,
};
enum nl80211_cqm_rssi_threshold_event {
@@ -230,17 +235,26 @@ enum nl80211_ext_feature {
NL80211_EXT_FEATURE_BEACON_PROTECTION,
NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT,
NL80211_EXT_FEATURE_PUNCT,
+ NL80211_EXT_FEATURE_DFS_CONCURRENT,
+ NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS,
+ NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT,
+ NL80211_EXT_FEATURE_SECURE_LTF,
+ NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE,
+ NL80211_EXT_FEATURE_4WAY_HANDSHAKE_AP_PSK,
+ NL80211_EXT_FEATURE_SAE_OFFLOAD_AP,
/* Keep this last. */
NUM_NL80211_EXT_FEATURES
};
+/* Keep in order with lkpi_nl80211_sta_info_to_str() */
enum nl80211_sta_info {
/* XXX TODO */
NL80211_STA_INFO_BEACON_RX,
NL80211_STA_INFO_BEACON_SIGNAL_AVG,
NL80211_STA_INFO_BSS_PARAM,
NL80211_STA_INFO_CHAIN_SIGNAL,
+ NL80211_STA_INFO_CHAIN_SIGNAL_AVG,
NL80211_STA_INFO_CONNECTED_TIME,
NL80211_STA_INFO_INACTIVE_TIME,
NL80211_STA_INFO_SIGNAL,
@@ -416,6 +430,13 @@ enum nl80211_user_reg_hint_type {
NL80211_USER_REG_HINT_USER,
};
+enum nl80211_hidden_ssid {
+ NL80211_HIDDEN_SSID_NOT_IN_USE,
+};
+
+#define NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY 16
+#define NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY 24
+
#define NL80211_KCK_LEN 16
#define NL80211_KCK_EXT_LEN 24
#define NL80211_KEK_LEN 16
diff --git a/sys/compat/linuxkpi/common/include/linux/nodemask.h b/sys/compat/linuxkpi/common/include/linux/nodemask.h
new file mode 100644
index 000000000000..7a245cc6f256
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/nodemask.h
@@ -0,0 +1,46 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Serenity Cyber Security, LLC.
+ *
+ * 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_NODEMASK_H_
+#define _LINUXKPI_LINUX_NODEMASK_H_
+
+#include <linux/kernel.h> /* pr_debug */
+
+static inline int
+num_online_nodes(void)
+{
+ return (1);
+}
+
+static inline int
+num_possible_nodes(void)
+{
+ pr_debug("%s: TODO\n", __func__);
+ return (1);
+}
+
+#endif /* _LINUXKPI_LINUX_NODEMASK_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/page-flags.h b/sys/compat/linuxkpi/common/include/linux/page-flags.h
index 9dd49c8492a5..a22b3a24c330 100644
--- a/sys/compat/linuxkpi/common/include/linux/page-flags.h
+++ b/sys/compat/linuxkpi/common/include/linux/page-flags.h
@@ -29,6 +29,13 @@
#ifndef _LINUXKPI_LINUX_PAGEFLAGS_H_
#define _LINUXKPI_LINUX_PAGEFLAGS_H_
+#include <linux/mm_types.h>
+
#define PageHighMem(p) (0)
+#define page_folio(p) \
+ (_Generic((p), \
+ const struct page *: (const struct folio *)(p), \
+ struct page *: (struct folio *)(p)))
+
#endif /* _LINUXKPI_LINUX_PAGEFLAGS_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/page.h b/sys/compat/linuxkpi/common/include/linux/page.h
index aa5acac4f5fe..37ab593a64e9 100644
--- a/sys/compat/linuxkpi/common/include/linux/page.h
+++ b/sys/compat/linuxkpi/common/include/linux/page.h
@@ -72,6 +72,7 @@ pgprot2cachemode(pgprot_t prot)
return (VM_MEMATTR_DEFAULT);
}
+#define page_to_virt(page) linux_page_address(page)
#define virt_to_page(x) PHYS_TO_VM_PAGE(vtophys(x))
#define page_to_pfn(pp) (VM_PAGE_TO_PHYS(pp) >> PAGE_SHIFT)
#define pfn_to_page(pfn) (PHYS_TO_VM_PAGE((pfn) << PAGE_SHIFT))
diff --git a/sys/compat/linuxkpi/common/include/linux/pagemap.h b/sys/compat/linuxkpi/common/include/linux/pagemap.h
index 7244b61257dc..cb6a1820ea8b 100644
--- a/sys/compat/linuxkpi/common/include/linux/pagemap.h
+++ b/sys/compat/linuxkpi/common/include/linux/pagemap.h
@@ -33,6 +33,8 @@
#include <linux/highmem.h>
#include <linux/vmalloc.h>
+struct folio_batch;
+
#define invalidate_mapping_pages(...) \
linux_invalidate_mapping_pages(__VA_ARGS__)
@@ -40,15 +42,6 @@ unsigned long linux_invalidate_mapping_pages(vm_object_t obj, pgoff_t start,
pgoff_t end);
static inline void
-release_pages(struct page **pages, int nr)
-{
- int i;
-
- for (i = 0; i < nr; i++)
- put_page(pages[i]);
-}
-
-static inline void
mapping_clear_unevictable(vm_object_t mapping)
{
}
diff --git a/sys/compat/linuxkpi/common/include/linux/pagevec.h b/sys/compat/linuxkpi/common/include/linux/pagevec.h
index 9ba8ff8effa0..0a952e965b5a 100644
--- a/sys/compat/linuxkpi/common/include/linux/pagevec.h
+++ b/sys/compat/linuxkpi/common/include/linux/pagevec.h
@@ -66,4 +66,72 @@ check_move_unevictable_pages(struct pagevec *pvec)
{
}
+/*
+ * struct folio
+ *
+ * On Linux, `struct folio` replaces `struct page`. To manage a list of folios,
+ * there is `struct folio_batch` on top of this, which replaces `struct
+ * pagevec` above.
+ *
+ * Here is the original description when `struct folio` was added to the Linux
+ * kernel:
+ * "A struct folio is a new abstraction to replace the venerable struct page.
+ * A function which takes a struct folio argument declares that it will
+ * operate on the entire (possibly compound) page, not just PAGE_SIZE bytes.
+ * In return, the caller guarantees that the pointer it is passing does not
+ * point to a tail page. No change to generated code."
+ */
+
+struct folio;
+
+struct folio_batch {
+ uint8_t nr;
+ struct folio *folios[PAGEVEC_SIZE];
+};
+
+static inline void
+folio_batch_init(struct folio_batch *fbatch)
+{
+ fbatch->nr = 0;
+}
+
+static inline void
+folio_batch_reinit(struct folio_batch *fbatch)
+{
+ fbatch->nr = 0;
+}
+
+static inline unsigned int
+folio_batch_count(struct folio_batch *fbatch)
+{
+ return (fbatch->nr);
+}
+
+static inline unsigned int
+folio_batch_space(struct folio_batch *fbatch)
+{
+ return (PAGEVEC_SIZE - fbatch->nr);
+}
+
+static inline unsigned int
+folio_batch_add(struct folio_batch *fbatch, struct folio *folio)
+{
+ KASSERT(
+ fbatch->nr < PAGEVEC_SIZE,
+ ("struct folio_batch %p is full", fbatch));
+
+ fbatch->folios[fbatch->nr++] = folio;
+
+ return (folio_batch_space(fbatch));
+}
+
+void __folio_batch_release(struct folio_batch *fbatch);
+
+static inline void
+folio_batch_release(struct folio_batch *fbatch)
+{
+ if (folio_batch_count(fbatch))
+ __folio_batch_release(fbatch);
+}
+
#endif /* _LINUXKPI_LINUX_PAGEVEC_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/pci.h b/sys/compat/linuxkpi/common/include/linux/pci.h
index be3b13f07e53..af19829f1cbb 100644
--- a/sys/compat/linuxkpi/common/include/linux/pci.h
+++ b/sys/compat/linuxkpi/common/include/linux/pci.h
@@ -36,6 +36,7 @@
#define CONFIG_PCI_MSI
#include <linux/types.h>
+#include <linux/device/driver.h>
#include <sys/param.h>
#include <sys/bus.h>
@@ -59,6 +60,8 @@
#include <linux/pci_ids.h>
#include <linux/pm.h>
+#include <linux/kernel.h> /* pr_debug */
+
struct pci_device_id {
uint32_t vendor;
uint32_t device;
@@ -69,6 +72,10 @@ struct pci_device_id {
uintptr_t driver_data;
};
+#define MODULE_DEVICE_TABLE_BUS_pci(_bus, _table) \
+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) \
\
@@ -82,11 +89,10 @@ static driver_t _ ## _bus ## _ ## _table ## _driver = { \
0 \
}; \
\
-DRIVER_MODULE(lkpi_ ## _table, pci, _ ## _bus ## _ ## _table ## _driver,\
+DRIVER_MODULE(lkpi_ ## _table, _bus, _ ## _bus ## _ ## _table ## _driver,\
0, 0); \
\
-MODULE_PNP_INFO("U32:vendor;U32:device;V32:subvendor;V32:subdevice", \
- _bus, lkpi_ ## _table, _table, nitems(_table) - 1)
+MODULE_DEVICE_TABLE_BUS_ ## _bus(_bus, _table)
#define PCI_ANY_ID -1U
@@ -113,6 +119,10 @@ MODULE_PNP_INFO("U32:vendor;U32:device;V32:subvendor;V32:subdevice", \
#define PCI_COMMAND PCIR_COMMAND
#define PCI_COMMAND_INTX_DISABLE PCIM_CMD_INTxDIS
#define PCI_COMMAND_MEMORY PCIM_CMD_MEMEN
+#define PCI_PRIMARY_BUS PCIR_PRIBUS_1
+#define PCI_SECONDARY_BUS PCIR_SECBUS_1
+#define PCI_SUBORDINATE_BUS PCIR_SUBBUS_1
+#define PCI_SEC_LATENCY_TIMER PCIR_SECLAT_1
#define PCI_EXP_DEVCTL PCIER_DEVICE_CTL /* Device Control */
#define PCI_EXP_LNKCTL PCIER_LINK_CTL /* Link Control */
#define PCI_EXP_LNKCTL_ASPM_L0S PCIEM_LINK_CTL_ASPMC_L0S
@@ -223,6 +233,8 @@ typedef int pci_power_t;
extern const char *pci_power_names[6];
+#define PCI_ERR_UNCOR_STATUS PCIR_AER_UC_STATUS
+#define PCI_ERR_COR_STATUS PCIR_AER_COR_STATUS
#define PCI_ERR_ROOT_COMMAND PCIR_AER_ROOTERR_CMD
#define PCI_ERR_ROOT_ERR_SRC PCIR_AER_COR_SOURCE_ID
@@ -232,10 +244,14 @@ extern const char *pci_power_names[6];
#define PCI_L1SS_CTL1 0x8
#define PCI_L1SS_CTL1_L1SS_MASK 0xf
-#define PCI_IRQ_LEGACY 0x01
+#define PCI_IRQ_INTX 0x01
#define PCI_IRQ_MSI 0x02
#define PCI_IRQ_MSIX 0x04
-#define PCI_IRQ_ALL_TYPES (PCI_IRQ_MSIX|PCI_IRQ_MSI|PCI_IRQ_LEGACY)
+#define PCI_IRQ_ALL_TYPES (PCI_IRQ_MSIX|PCI_IRQ_MSI|PCI_IRQ_INTX)
+
+#if defined(LINUXKPI_VERSION) && (LINUXKPI_VERSION <= 61000)
+#define PCI_IRQ_LEGACY PCI_IRQ_INTX
+#endif
struct pci_dev;
@@ -274,24 +290,8 @@ extern spinlock_t pci_lock;
#define __devexit_p(x) x
-#define module_pci_driver(_driver) \
- \
-static inline int \
-_pci_init(void) \
-{ \
- \
- return (linux_pci_register_driver(&_driver)); \
-} \
- \
-static inline void \
-_pci_exit(void) \
-{ \
- \
- linux_pci_unregister_driver(&_driver); \
-} \
- \
-module_init(_pci_init); \
-module_exit(_pci_exit)
+#define module_pci_driver(_drv) \
+ module_driver(_drv, linux_pci_register_driver, linux_pci_unregister_driver)
struct msi_msg {
uint32_t data;
@@ -362,6 +362,8 @@ bool pci_device_is_present(struct pci_dev *pdev);
int linuxkpi_pcim_enable_device(struct pci_dev *pdev);
void __iomem **linuxkpi_pcim_iomap_table(struct pci_dev *pdev);
+void *linuxkpi_pci_iomap_range(struct pci_dev *pdev, int mmio_bar,
+ unsigned long mmio_off, unsigned long mmio_size);
void *linuxkpi_pci_iomap(struct pci_dev *pdev, int mmio_bar, int mmio_size);
void linuxkpi_pci_iounmap(struct pci_dev *pdev, void *res);
int linuxkpi_pcim_iomap_regions(struct pci_dev *pdev, uint32_t mask,
@@ -380,6 +382,11 @@ struct msi_desc *lkpi_pci_msi_desc_alloc(int);
struct device *lkpi_pci_find_irq_dev(unsigned int irq);
int _lkpi_pci_enable_msi_range(struct pci_dev *pdev, int minvec, int maxvec);
+#define pci_err(pdev, fmt, ...) \
+ dev_err(&(pdev)->dev, fmt, ##__VA_ARGS__)
+#define pci_info(pdev, fmt, ...) \
+ dev_info(&(pdev)->dev, fmt, ##__VA_ARGS__)
+
static inline bool
dev_is_pci(struct device *dev)
{
@@ -521,7 +528,20 @@ pci_upstream_bridge(struct pci_dev *pdev)
if (pdev == pdev->bus->self) {
device_t bridge;
- bridge = device_get_parent(pdev->dev.bsddev);
+ /*
+ * In the case of DRM drivers, the passed device is a child of
+ * `vgapci`. We want to start the lookup from `vgapci`, so the
+ * parent of the passed `drmn`.
+ *
+ * We can use the `isdrm` flag to determine this.
+ */
+ bridge = pdev->dev.bsddev;
+ if (pdev->pdrv != NULL && pdev->pdrv->isdrm)
+ bridge = device_get_parent(bridge);
+ if (bridge == NULL)
+ goto done;
+
+ bridge = device_get_parent(bridge);
if (bridge == NULL)
goto done;
bridge = device_get_parent(bridge);
@@ -774,6 +794,8 @@ static inline void pci_disable_sriov(struct pci_dev *dev)
{
}
+#define pci_iomap_range(pdev, mmio_bar, mmio_off, mmio_size) \
+ linuxkpi_pci_iomap_range(pdev, mmio_bar, mmio_off, mmio_size)
#define pci_iomap(pdev, mmio_bar, mmio_size) \
linuxkpi_pci_iomap(pdev, mmio_bar, mmio_size)
#define pci_iounmap(pdev, res) linuxkpi_pci_iounmap(pdev, res)
@@ -1268,6 +1290,29 @@ pci_dev_present(const struct pci_device_id *cur)
return (0);
}
+static inline const struct pci_device_id *
+pci_match_id(const struct pci_device_id *ids, struct pci_dev *pdev)
+{
+ if (ids == NULL)
+ return (NULL);
+
+ for (;
+ ids->vendor != 0 || ids->subvendor != 0 || ids->class_mask != 0;
+ ids++)
+ if ((ids->vendor == PCI_ANY_ID ||
+ ids->vendor == pdev->vendor) &&
+ (ids->device == PCI_ANY_ID ||
+ ids->device == pdev->device) &&
+ (ids->subvendor == PCI_ANY_ID ||
+ ids->subvendor == pdev->subsystem_vendor) &&
+ (ids->subdevice == PCI_ANY_ID ||
+ ids->subdevice == pdev->subsystem_device) &&
+ ((ids->class ^ pdev->class) & ids->class_mask) == 0)
+ return (ids);
+
+ return (NULL);
+}
+
struct pci_dev *lkpi_pci_get_domain_bus_and_slot(int domain,
unsigned int bus, unsigned int devfn);
#define pci_get_domain_bus_and_slot(domain, bus, devfn) \
@@ -1336,6 +1381,9 @@ pci_bus_write_config_word(struct pci_bus *bus, unsigned int devfn, int pos,
struct pci_dev *lkpi_pci_get_class(unsigned int class, struct pci_dev *from);
#define pci_get_class(class, from) lkpi_pci_get_class(class, from)
+struct pci_dev *lkpi_pci_get_base_class(unsigned int class,
+ struct pci_dev *from);
+#define pci_get_base_class(class, from) lkpi_pci_get_base_class(class, from)
/* -------------------------------------------------------------------------- */
@@ -1478,4 +1526,12 @@ pci_irq_vector(struct pci_dev *pdev, unsigned int vector)
return (-ENXIO);
}
+static inline int
+pci_wake_from_d3(struct pci_dev *pdev, bool enable)
+{
+
+ pr_debug("%s: TODO\n", __func__);
+ return (0);
+}
+
#endif /* _LINUXKPI_LINUX_PCI_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/pci_ids.h b/sys/compat/linuxkpi/common/include/linux/pci_ids.h
index 2f02d6ad1c14..e318f6f75ce7 100644
--- a/sys/compat/linuxkpi/common/include/linux/pci_ids.h
+++ b/sys/compat/linuxkpi/common/include/linux/pci_ids.h
@@ -46,6 +46,7 @@
#define PCI_VENDOR_ID_APPLE 0x106b
#define PCI_VENDOR_ID_ASUSTEK 0x1043
+#define PCI_VENDOR_ID_ASMEDIA 0x1b21
#define PCI_VENDOR_ID_ATHEROS 0x168c
#define PCI_VENDOR_ID_ATI 0x1002
#define PCI_VENDOR_ID_BROADCOM 0x14e4
diff --git a/sys/compat/linuxkpi/common/include/linux/perf_event.h b/sys/compat/linuxkpi/common/include/linux/perf_event.h
new file mode 100644
index 000000000000..86b0d06cdc1f
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/perf_event.h
@@ -0,0 +1,34 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 Jean-Sébastien Pédron <dumbbell@FreeBSD.org>
+ *
+ * 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_PERF_EVENT_H_
+#define _LINUXKPI_LINUX_PERF_EVENT_H_
+
+#include <linux/cgroup.h>
+
+#endif /* _LINUXKPI_LINUX_PERF_EVENT_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/pm.h b/sys/compat/linuxkpi/common/include/linux/pm.h
index 871c7b587864..c8d943027909 100644
--- a/sys/compat/linuxkpi/common/include/linux/pm.h
+++ b/sys/compat/linuxkpi/common/include/linux/pm.h
@@ -1,7 +1,7 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2020 The FreeBSD Foundation
+ * Copyright (c) 2020-2024 The FreeBSD Foundation
*
* This software was developed by Björn Zeeb under sponsorship from
* the FreeBSD Foundation.
@@ -58,25 +58,26 @@ struct dev_pm_info {
IS_ENABLED(CONFIG_PM_SLEEP) ? (_p) : NULL
#ifdef CONFIG_PM_SLEEP
+#define __SET_PM_OPS(_suspendfunc, _resumefunc) \
+ .suspend = _suspendfunc, \
+ .resume = _resumefunc, \
+ .freeze = _suspendfunc, \
+ .thaw = _resumefunc, \
+ .poweroff = _suspendfunc, \
+ .restore = _resumefunc, \
+
#define SIMPLE_DEV_PM_OPS(_name, _suspendfunc, _resumefunc) \
const struct dev_pm_ops _name = { \
- .suspend = _suspendfunc, \
- .resume = _resumefunc, \
- .freeze = _suspendfunc, \
- .thaw = _resumefunc, \
- .poweroff = _suspendfunc, \
- .restore = _resumefunc, \
+ __SET_PM_OPS(_suspendfunc, _resumefunc) \
}
#define DEFINE_SIMPLE_DEV_PM_OPS(_name, _suspendfunc, _resumefunc) \
const struct dev_pm_ops _name = { \
- .suspend = _suspendfunc, \
- .resume = _resumefunc, \
- .freeze = _suspendfunc, \
- .thaw = _resumefunc, \
- .poweroff = _suspendfunc, \
- .restore = _resumefunc, \
+ __SET_PM_OPS(_suspendfunc, _resumefunc) \
}
+
+#define SET_SYSTEM_SLEEP_PM_OPS(_suspendfunc, _resumefunc) \
+ __SET_PM_OPS(_suspendfunc, _resumefunc)
#else
#define SIMPLE_DEV_PM_OPS(_name, _suspendfunc, _resumefunc) \
const struct dev_pm_ops _name = { \
@@ -86,6 +87,9 @@ const struct dev_pm_ops _name = { \
}
#endif
+bool linuxkpi_device_can_wakeup(struct device *);
+#define device_can_wakeup(_dev) linuxkpi_device_can_wakeup(_dev)
+
static inline void
pm_wakeup_event(struct device *dev __unused, unsigned int x __unused)
{
diff --git a/sys/compat/linuxkpi/common/include/linux/pm_runtime.h b/sys/compat/linuxkpi/common/include/linux/pm_runtime.h
index 616dd508e562..6114b7b159d7 100644
--- a/sys/compat/linuxkpi/common/include/linux/pm_runtime.h
+++ b/sys/compat/linuxkpi/common/include/linux/pm_runtime.h
@@ -34,8 +34,13 @@ pm_runtime_get_if_in_use(struct device *dev)
return 1;
}
+#if defined(LINUXKPI_VERSION) && LINUXKPI_VERSION < 60900
static inline int
pm_runtime_get_if_active(struct device *dev, bool x)
+#else
+static inline int
+pm_runtime_get_if_active(struct device *dev)
+#endif
{
return 1;
}
diff --git a/sys/compat/linuxkpi/common/include/linux/printk.h b/sys/compat/linuxkpi/common/include/linux/printk.h
index 933d5aa6f94a..da9d45122d4d 100644
--- a/sys/compat/linuxkpi/common/include/linux/printk.h
+++ b/sys/compat/linuxkpi/common/include/linux/printk.h
@@ -44,57 +44,19 @@ enum {
DUMP_PREFIX_OFFSET
};
+int __lkpi_hexdump_printf(void *, const char *, ...) __printflike(2, 3);
+
+void lkpi_hex_dump(int(*)(void *, const char *, ...), void *arg1,
+ const char *, const char *, const int, const int, const int,
+ const void *, size_t, const bool);
+
static inline void
print_hex_dump(const char *level, const char *prefix_str,
const int prefix_type, const int rowsize, const int groupsize,
const void *buf, size_t len, const bool ascii)
{
- typedef const struct { long long value; } __packed *print_64p_t;
- typedef const struct { uint32_t value; } __packed *print_32p_t;
- typedef const struct { uint16_t value; } __packed *print_16p_t;
- const void *buf_old = buf;
- int row;
-
- while (len > 0) {
- if (level != NULL)
- printf("%s", level);
- if (prefix_str != NULL)
- printf("%s ", prefix_str);
-
- switch (prefix_type) {
- case DUMP_PREFIX_ADDRESS:
- printf("[%p] ", buf);
- break;
- case DUMP_PREFIX_OFFSET:
- printf("[%#tx] ", ((const char *)buf -
- (const char *)buf_old));
- break;
- default:
- break;
- }
- for (row = 0; row != rowsize; row++) {
- if (groupsize == 8 && len > 7) {
- printf("%016llx ", ((print_64p_t)buf)->value);
- buf = (const uint8_t *)buf + 8;
- len -= 8;
- } else if (groupsize == 4 && len > 3) {
- printf("%08x ", ((print_32p_t)buf)->value);
- buf = (const uint8_t *)buf + 4;
- len -= 4;
- } else if (groupsize == 2 && len > 1) {
- printf("%04x ", ((print_16p_t)buf)->value);
- buf = (const uint8_t *)buf + 2;
- len -= 2;
- } else if (len > 0) {
- printf("%02x ", *(const uint8_t *)buf);
- buf = (const uint8_t *)buf + 1;
- len--;
- } else {
- break;
- }
- }
- printf("\n");
- }
+ lkpi_hex_dump(__lkpi_hexdump_printf, NULL, level, prefix_str, prefix_type,
+ rowsize, groupsize, buf, len, ascii);
}
static inline void
@@ -125,4 +87,11 @@ print_hex_dump_bytes(const char *prefix_str, const int prefix_type,
#define pr_info_ratelimited(fmt, ...) \
printk_ratelimited(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
+#define no_printk(fmt, ...) \
+({ \
+ if (0) \
+ printk(pr_fmt(fmt), ##__VA_ARGS__); \
+ 0; \
+})
+
#endif /* _LINUXKPI_LINUX_PRINTK_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/pwm.h b/sys/compat/linuxkpi/common/include/linux/pwm.h
index 59a17f525c91..c0740db675e8 100644
--- a/sys/compat/linuxkpi/common/include/linux/pwm.h
+++ b/sys/compat/linuxkpi/common/include/linux/pwm.h
@@ -91,4 +91,10 @@ pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state)
return (-ENOTSUPP);
}
+static inline int
+pwm_apply_might_sleep(struct pwm_device *pwm, const struct pwm_state *state)
+{
+ return (0);
+}
+
#endif /* _LINUXKPI_LINUX_PWM_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/radix-tree.h b/sys/compat/linuxkpi/common/include/linux/radix-tree.h
index a1204cb20a79..ea75836c26fb 100644
--- a/sys/compat/linuxkpi/common/include/linux/radix-tree.h
+++ b/sys/compat/linuxkpi/common/include/linux/radix-tree.h
@@ -29,6 +29,7 @@
#ifndef _LINUXKPI_LINUX_RADIX_TREE_H_
#define _LINUXKPI_LINUX_RADIX_TREE_H_
+#include <linux/rcupdate.h>
#include <linux/types.h>
#define RADIX_TREE_MAP_SHIFT 6
diff --git a/sys/compat/linuxkpi/common/include/linux/random.h b/sys/compat/linuxkpi/common/include/linux/random.h
index 808c5bc55974..893ee2b7b728 100644
--- a/sys/compat/linuxkpi/common/include/linux/random.h
+++ b/sys/compat/linuxkpi/common/include/linux/random.h
@@ -54,6 +54,15 @@ get_random_int(void)
return (val);
}
+static inline uint8_t
+get_random_u8(void)
+{
+ uint8_t val;
+
+ get_random_bytes(&val, sizeof(val));
+ return (val);
+}
+
#define get_random_u32() get_random_int()
/*
diff --git a/sys/compat/linuxkpi/common/include/linux/rbtree.h b/sys/compat/linuxkpi/common/include/linux/rbtree.h
index 78bf938eb000..e6033cfd760d 100644
--- a/sys/compat/linuxkpi/common/include/linux/rbtree.h
+++ b/sys/compat/linuxkpi/common/include/linux/rbtree.h
@@ -175,6 +175,30 @@ rb_replace_node_cached(struct rb_node *old, struct rb_node *new,
root->rb_leftmost = new;
}
+static inline struct rb_node *
+rb_add_cached(struct rb_node *node, struct rb_root_cached *tree,
+ bool (*less)(struct rb_node *, const struct rb_node *))
+{
+ struct rb_node **link = &tree->rb_root.rb_node;
+ struct rb_node *parent = NULL;
+ bool leftmost = true;
+
+ while (*link != NULL) {
+ parent = *link;
+ if (less(node, parent)) {
+ link = &RB_LEFT(parent, __entry);
+ } else {
+ link = &RB_RIGHT(parent, __entry);
+ leftmost = false;
+ }
+ }
+
+ rb_link_node(node, parent, link);
+ rb_insert_color_cached(node, tree, leftmost);
+
+ return (leftmost ? node : NULL);
+}
+
#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/rcupdate.h b/sys/compat/linuxkpi/common/include/linux/rcupdate.h
index 95332217f8f5..85d766c8dbc9 100644
--- a/sys/compat/linuxkpi/common/include/linux/rcupdate.h
+++ b/sys/compat/linuxkpi/common/include/linux/rcupdate.h
@@ -1,6 +1,10 @@
/*-
* Copyright (c) 2016-2017 Mellanox Technologies, Ltd.
* All rights reserved.
+ * Copyright (c) 2024 The FreeBSD Foundation
+ *
+ * Portions of this software were developed by Björn Zeeb
+ * 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
@@ -26,11 +30,20 @@
#ifndef _LINUXKPI_LINUX_RCUPDATE_H_
#define _LINUXKPI_LINUX_RCUPDATE_H_
+#include <sys/cdefs.h>
+
#include <linux/compiler.h>
#include <linux/types.h>
+#include <linux/kernel.h>
#include <machine/atomic.h>
+extern int linuxkpi_rcu_debug;
+#define RCU_WARN_ONCE(c, ...) do { \
+ if (unlikely(linuxkpi_rcu_debug > 0)) \
+ WARN_ONCE((c), ##__VA_ARGS__); \
+} while(0)
+
#define LINUX_KFREE_RCU_OFFSET_MAX 4096 /* exclusive */
/* BSD specific defines */
@@ -61,6 +74,9 @@
linux_rcu_read_unlock(RCU_TYPE_REGULAR);\
} while (0)
+#define rcu_read_lock_held(void) \
+ linux_rcu_read_lock_held(RCU_TYPE_REGULAR)
+
#define synchronize_rcu(void) do { \
linux_synchronize_rcu(RCU_TYPE_REGULAR); \
} while (0)
@@ -79,14 +95,34 @@
#define rcu_access_pointer(p) \
((__typeof(*p) *)READ_ONCE(p))
-#define rcu_dereference_protected(p, c) \
+#define rcu_dereference(p) \
((__typeof(*p) *)READ_ONCE(p))
-#define rcu_dereference(p) \
- rcu_dereference_protected(p, 0)
+#define __rcu_var_name(n, f, l) \
+ __CONCAT(__CONCAT(__CONCAT(rcu_, n), _), __COUNTER__)
+
+#define __rcu_dereference_protected(p, c, n) \
+({ \
+ RCU_WARN_ONCE(!(c), "%s:%d: condition for %s failed\n", \
+ __func__, __LINE__, __XSTRING(n)); \
+ rcu_dereference(p); \
+})
+
+#define rcu_dereference_protected(p, c) \
+ __rcu_dereference_protected((p), (c), \
+ __rcu_var_name(protected, __func__, __LINE__))
+
+#define __rcu_dereference_check(p, c, n) \
+({ \
+ __typeof(*p) *n = rcu_dereference(p); \
+ RCU_WARN_ONCE(!(c), "%s:%d: condition for %s failed\n", \
+ __func__, __LINE__, __XSTRING(n)); \
+ n; \
+})
-#define rcu_dereference_check(p, c) \
- rcu_dereference_protected(p, c)
+#define rcu_dereference_check(p, c) \
+ __rcu_dereference_check((p), (c) || rcu_read_lock_held(), \
+ __rcu_var_name(check, __func__, __LINE__))
#define rcu_dereference_raw(p) \
((__typeof(*p) *)READ_ONCE(p))
@@ -113,11 +149,12 @@
/* prototypes */
-extern void linux_call_rcu(unsigned type, struct rcu_head *ptr, rcu_callback_t func);
-extern void linux_rcu_barrier(unsigned type);
-extern void linux_rcu_read_lock(unsigned type);
-extern void linux_rcu_read_unlock(unsigned type);
-extern void linux_synchronize_rcu(unsigned type);
+void linux_call_rcu(unsigned type, struct rcu_head *ptr, rcu_callback_t func);
+void linux_rcu_barrier(unsigned type);
+void linux_rcu_read_lock(unsigned type);
+void linux_rcu_read_unlock(unsigned type);
+bool linux_rcu_read_lock_held(unsigned);
+void linux_synchronize_rcu(unsigned type);
/* Empty implementation for !DEBUG */
#define init_rcu_head(...)
diff --git a/sys/compat/linuxkpi/common/include/linux/ref_tracker.h b/sys/compat/linuxkpi/common/include/linux/ref_tracker.h
new file mode 100644
index 000000000000..fa510b2498e1
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/ref_tracker.h
@@ -0,0 +1,93 @@
+/*-
+ * 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_REF_TRACKER_H_
+#define _LINUXKPI_LINUX_REF_TRACKER_H_
+
+#include <linux/refcount.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/stackdepot.h>
+
+struct ref_tracker;
+
+struct ref_tracker_dir {
+};
+
+/*
+ * The following functions currently have dummy implementations that, on Linux,
+ * are used when CONFIG_REF_TRACKER is not set at compile time.
+ *
+ * The ref tracker is a tool to associate a refcount increase to a refcount
+ * decrease. This helps developers track, document and debug refcounts. We
+ * don't need this feature for now in linuxkpi.
+ */
+
+static inline void
+ref_tracker_dir_init(struct ref_tracker_dir *dir,
+ unsigned int quarantine_count, const char *name)
+{
+}
+
+static inline void
+ref_tracker_dir_exit(struct ref_tracker_dir *dir)
+{
+}
+
+static inline void
+ref_tracker_dir_print_locked(struct ref_tracker_dir *dir,
+ unsigned int display_limit)
+{
+}
+
+static inline void
+ref_tracker_dir_print(struct ref_tracker_dir *dir, unsigned int display_limit)
+{
+}
+
+static inline int
+ref_tracker_dir_snprint(struct ref_tracker_dir *dir, char *buf, size_t size)
+{
+ return (0);
+}
+
+static inline int
+ref_tracker_alloc(struct ref_tracker_dir *dir, struct ref_tracker **trackerp,
+ gfp_t gfp)
+{
+ return (0);
+}
+
+static inline int
+ref_tracker_free(struct ref_tracker_dir *dir, struct ref_tracker **trackerp)
+{
+ return (0);
+}
+
+#endif /* !defined(_LINUXKPI_LINUX_REF_TRACKER_H_) */
diff --git a/sys/compat/linuxkpi/common/include/linux/refcount.h b/sys/compat/linuxkpi/common/include/linux/refcount.h
index 61947485945d..02a7eda3f4a9 100644
--- a/sys/compat/linuxkpi/common/include/linux/refcount.h
+++ b/sys/compat/linuxkpi/common/include/linux/refcount.h
@@ -31,58 +31,51 @@
#include <linux/atomic.h>
-struct refcount_linux {
- atomic_t value;
-};
-typedef struct refcount_linux refcount_t;
+typedef atomic_t refcount_t;
static inline void
refcount_set(refcount_t *ref, unsigned int i)
{
- atomic_set(&ref->value, i);
+ atomic_set(ref, i);
}
static inline void
refcount_inc(refcount_t *ref)
{
- atomic_inc(&ref->value);
+ atomic_inc(ref);
}
static inline bool
refcount_inc_not_zero(refcount_t *ref)
{
- return (atomic_inc_not_zero(&ref->value));
+ return (atomic_inc_not_zero(ref));
}
static inline void
refcount_dec(refcount_t *ref)
{
- atomic_dec(&ref->value);
+ atomic_dec(ref);
}
static inline unsigned int
refcount_read(refcount_t *ref)
{
- return atomic_read(&ref->value);
+ return atomic_read(ref);
}
static inline bool
refcount_dec_and_lock_irqsave(refcount_t *ref, spinlock_t *lock,
unsigned long *flags)
{
- if (atomic_dec_and_test(&ref->value) == true) {
+ if (atomic_dec_and_test(ref) == true) {
spin_lock_irqsave(lock, flags);
return (true);
}
return (false);
}
-/*
- * struct kref uses atomic_t and not refcount_t so
- * we differ from Linux here.
- */
static inline bool
-refcount_dec_and_test(atomic_t *r)
+refcount_dec_and_test(refcount_t *r)
{
return (atomic_dec_and_test(r));
diff --git a/sys/compat/linuxkpi/common/include/linux/rwlock.h b/sys/compat/linuxkpi/common/include/linux/rwlock.h
index 8c1ee36ac4de..3030ec89ff1e 100644
--- a/sys/compat/linuxkpi/common/include/linux/rwlock.h
+++ b/sys/compat/linuxkpi/common/include/linux/rwlock.h
@@ -34,14 +34,12 @@
#include <sys/rwlock.h>
#include <sys/libkern.h>
-typedef struct {
- struct rwlock rw;
-} rwlock_t;
+typedef struct rwlock rwlock_t;
-#define read_lock(_l) rw_rlock(&(_l)->rw)
-#define write_lock(_l) rw_wlock(&(_l)->rw)
-#define read_unlock(_l) rw_runlock(&(_l)->rw)
-#define write_unlock(_l) rw_wunlock(&(_l)->rw)
+#define read_lock(_l) rw_rlock(_l)
+#define write_lock(_l) rw_wlock(_l)
+#define read_unlock(_l) rw_runlock(_l)
+#define write_unlock(_l) rw_wunlock(_l)
#define read_lock_irq(lock) read_lock((lock))
#define read_unlock_irq(lock) read_unlock((lock))
#define write_lock_irq(lock) write_lock((lock))
@@ -54,13 +52,6 @@ typedef struct {
do { read_unlock(lock); } while (0)
#define write_unlock_irqrestore(lock, flags) \
do { write_unlock(lock); } while (0)
-
-static inline void
-rwlock_init(rwlock_t *lock)
-{
-
- memset(&lock->rw, 0, sizeof(lock->rw));
- rw_init_flags(&lock->rw, "lnxrw", RW_NOWITNESS);
-}
+#define rwlock_init(_l) rw_init_flags(_l, "lnxrw", RW_NOWITNESS | RW_NEW)
#endif /* _LINUXKPI_LINUX_RWLOCK_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/scatterlist.h b/sys/compat/linuxkpi/common/include/linux/scatterlist.h
index e462d5c649f1..537f5bebc5aa 100644
--- a/sys/compat/linuxkpi/common/include/linux/scatterlist.h
+++ b/sys/compat/linuxkpi/common/include/linux/scatterlist.h
@@ -32,8 +32,11 @@
#define _LINUXKPI_LINUX_SCATTERLIST_H_
#include <sys/types.h>
+#include <sys/proc.h>
+#include <sys/sched.h>
#include <sys/sf_buf.h>
+#include <linux/err.h>
#include <linux/page.h>
#include <linux/slab.h>
#include <linux/mm.h>
@@ -671,4 +674,11 @@ sg_pcopy_to_buffer(struct scatterlist *sgl, unsigned int nents,
return (total);
}
+static inline void
+sg_set_folio(struct scatterlist *sg, struct folio *folio, size_t len,
+ size_t offset)
+{
+ sg_set_page(sg, &folio->page, len, offset);
+}
+
#endif /* _LINUXKPI_LINUX_SCATTERLIST_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/sched.h b/sys/compat/linuxkpi/common/include/linux/sched.h
index 8cb6b12100d5..3ad2f8e4ce8b 100644
--- a/sys/compat/linuxkpi/common/include/linux/sched.h
+++ b/sys/compat/linuxkpi/common/include/linux/sched.h
@@ -42,6 +42,7 @@
#include <linux/completion.h>
#include <linux/hrtimer.h>
#include <linux/mm_types.h>
+#include <linux/nodemask.h>
#include <linux/pid.h>
#include <linux/slab.h>
#include <linux/string.h>
@@ -52,7 +53,7 @@
#include <asm/atomic.h>
-#define MAX_SCHEDULE_TIMEOUT INT_MAX
+#define MAX_SCHEDULE_TIMEOUT LONG_MAX
#define TASK_RUNNING 0x0000
#define TASK_INTERRUPTIBLE 0x0001
@@ -159,7 +160,7 @@ void linux_send_sig(int signo, struct task_struct *task);
linux_send_sig(signo, task); \
} while (0)
-int linux_schedule_timeout(int timeout);
+long linux_schedule_timeout(long timeout);
static inline void
linux_schedule_save_interrupt_value(struct task_struct *task, int value)
diff --git a/sys/compat/linuxkpi/common/include/linux/seq_file.h b/sys/compat/linuxkpi/common/include/linux/seq_file.h
index d8b327f59538..876ef9e8dfe5 100644
--- a/sys/compat/linuxkpi/common/include/linux/seq_file.h
+++ b/sys/compat/linuxkpi/common/include/linux/seq_file.h
@@ -28,9 +28,13 @@
#ifndef _LINUXKPI_LINUX_SEQ_FILE_H_
#define _LINUXKPI_LINUX_SEQ_FILE_H_
+#include <sys/types.h>
+#include <sys/sbuf.h>
+
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/string_helpers.h>
+#include <linux/printk.h>
#undef file
#define inode vnode
@@ -89,6 +93,16 @@ void lkpi_seq_printf(struct seq_file *m, const char *fmt, ...);
#define seq_vprintf(...) lkpi_seq_vprintf(__VA_ARGS__)
#define seq_printf(...) lkpi_seq_printf(__VA_ARGS__)
+int __lkpi_hexdump_sbuf_printf(void *, const char *, ...) __printflike(2, 3);
+
+static inline void
+seq_hex_dump(struct seq_file *m, const char *prefix_str, int prefix_type,
+ int rowsize, int groupsize, const void *buf, size_t len, bool ascii)
+{
+ lkpi_hex_dump(__lkpi_hexdump_sbuf_printf, m->buf, NULL, prefix_str, prefix_type,
+ rowsize, groupsize, buf, len, ascii);
+}
+
#define file linux_file
#endif /* _LINUXKPI_LINUX_SEQ_FILE_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/seqlock.h b/sys/compat/linuxkpi/common/include/linux/seqlock.h
index 48e42efc10fe..554fdfd6e202 100644
--- a/sys/compat/linuxkpi/common/include/linux/seqlock.h
+++ b/sys/compat/linuxkpi/common/include/linux/seqlock.h
@@ -99,7 +99,7 @@ lkpi_write_seqcount_invalidate(seqc_t *seqcp)
static inline seqc_t
lkpi_seqprop_sequence(const seqc_t *seqcp)
{
- return (atomic_load_int(__DECONST(seqc_t *, seqcp)));
+ return (atomic_load_int(seqcp));
}
#define seqprop_sequence(s) lkpi_seqprop_sequence(&(s)->seqc)
diff --git a/sys/compat/linuxkpi/common/include/linux/shmem_fs.h b/sys/compat/linuxkpi/common/include/linux/shmem_fs.h
index efa2c855fe7d..5e91725d4a1c 100644
--- a/sys/compat/linuxkpi/common/include/linux/shmem_fs.h
+++ b/sys/compat/linuxkpi/common/include/linux/shmem_fs.h
@@ -54,4 +54,14 @@ void linux_shmem_truncate_range(vm_object_t obj, loff_t lstart,
#define shmem_truncate_range(...) \
linux_shmem_truncate_range(__VA_ARGS__)
+static inline struct folio *
+shmem_read_folio_gfp(vm_object_t obj, int pindex, gfp_t gfp)
+{
+ struct page *page;
+
+ page = shmem_read_mapping_page_gfp(obj, pindex, gfp);
+
+ return (page_folio(page));
+}
+
#endif /* _LINUXKPI_LINUX_SHMEM_FS_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/shrinker.h b/sys/compat/linuxkpi/common/include/linux/shrinker.h
index a865241cc7cb..eb95dafb83ce 100644
--- a/sys/compat/linuxkpi/common/include/linux/shrinker.h
+++ b/sys/compat/linuxkpi/common/include/linux/shrinker.h
@@ -27,6 +27,8 @@
#define _LINUXKPI_LINUX_SHRINKER_H_
#include <sys/queue.h>
+
+#include <linux/bitops.h>
#include <linux/gfp.h>
struct shrink_control {
@@ -39,6 +41,8 @@ struct shrinker {
unsigned long (*count_objects)(struct shrinker *, struct shrink_control *);
unsigned long (*scan_objects)(struct shrinker *, struct shrink_control *);
int seeks;
+ unsigned int flags;
+ void * private_data;
long batch;
TAILQ_ENTRY(shrinker) next;
};
@@ -47,10 +51,23 @@ struct shrinker {
#define DEFAULT_SEEKS 2
+#define SHRINKER_REGISTERED BIT(0)
+#define SHRINKER_ALLOCATED BIT(1)
+
+struct shrinker *linuxkpi_shrinker_alloc(
+ unsigned int flags, const char *fmt, ...);
int linuxkpi_register_shrinker(struct shrinker *s);
void linuxkpi_unregister_shrinker(struct shrinker *s);
+void linuxkpi_shrinker_free(struct shrinker *shrinker);
void linuxkpi_synchronize_shrinkers(void);
+#define shrinker_alloc(flags, fmt, ...) \
+ linuxkpi_shrinker_alloc(flags, fmt __VA_OPT__(,) __VA_ARGS__)
+#define shrinker_register(shrinker) \
+ linuxkpi_register_shrinker(shrinker)
+#define shrinker_free(shrinker) \
+ linuxkpi_shrinker_free(shrinker)
+
#if defined(LINUXKPI_VERSION) && LINUXKPI_VERSION >= 60000
#define register_shrinker(s, ...) linuxkpi_register_shrinker(s)
#else
diff --git a/sys/compat/linuxkpi/common/include/linux/skbuff.h b/sys/compat/linuxkpi/common/include/linux/skbuff.h
index ee3f427aa6e9..c8ad90281e34 100644
--- a/sys/compat/linuxkpi/common/include/linux/skbuff.h
+++ b/sys/compat/linuxkpi/common/include/linux/skbuff.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2020-2023 The FreeBSD Foundation
+ * Copyright (c) 2020-2025 The FreeBSD Foundation
* Copyright (c) 2021-2023 Bjoern A. Zeeb
*
* This software was developed by Björn Zeeb under sponsorship from
@@ -45,8 +45,17 @@
#include <linux/compiler.h>
#include <linux/spinlock.h>
#include <linux/ktime.h>
+#include <linux/compiler.h>
+
+#include "opt_wlan.h"
+
+/* Currently this is only used for wlan so we can depend on that. */
+#if defined(IEEE80211_DEBUG) && !defined(SKB_DEBUG)
+#define SKB_DEBUG
+#endif
/* #define SKB_DEBUG */
+
#ifdef SKB_DEBUG
#define DSKB_TODO 0x01
#define DSKB_IMPROVE 0x02
@@ -145,37 +154,46 @@ struct sk_buff {
};
struct list_head list;
};
- uint32_t _alloc_len; /* Length of alloc data-buf. XXX-BZ give up for truesize? */
+
+ uint8_t *head; /* Head of buffer. */
+ uint8_t *data; /* Head of data. */
+ uint8_t *tail; /* End of data. */
+ uint8_t *end; /* End of buffer. */
+
uint32_t len; /* ? */
uint32_t data_len; /* ? If we have frags? */
+ union {
+ __wsum csum;
+ struct {
+ uint16_t csum_offset;
+ uint16_t csum_start;
+ };
+ };
+ uint16_t protocol;
+ uint8_t ip_summed;
+ /* uint8_t */
+
+ /* "Scratch" area for layers to store metadata. */
+ /* ??? I see sizeof() operations so probably an array. */
+ uint8_t cb[64] __aligned(CACHE_LINE_SIZE);
+
+ struct skb_shared_info *shinfo __aligned(CACHE_LINE_SIZE);
+
uint32_t truesize; /* The total size of all buffers, incl. frags. */
- uint16_t mac_len; /* Link-layer header length. */
- __sum16 csum;
- uint16_t l3hdroff; /* network header offset from *head */
- uint16_t l4hdroff; /* transport header offset from *head */
uint32_t priority;
uint16_t qmap; /* queue mapping */
uint16_t _flags; /* Internal flags. */
#define _SKB_FLAGS_SKBEXTFRAG 0x0001
- enum sk_buff_pkt_type pkt_type;
+ uint16_t l3hdroff; /* network header offset from *head */
+ uint16_t l4hdroff; /* transport header offset from *head */
uint16_t mac_header; /* offset of mac_header */
-
- /* "Scratch" area for layers to store metadata. */
- /* ??? I see sizeof() operations so probably an array. */
- uint8_t cb[64] __aligned(CACHE_LINE_SIZE);
+ uint16_t mac_len; /* Link-layer header length. */
+ enum sk_buff_pkt_type pkt_type;
+ refcount_t refcnt;
struct net_device *dev;
void *sk; /* XXX net/sock.h? */
- int csum_offset, csum_start, ip_summed, protocol;
-
- uint8_t *head; /* Head of buffer. */
- uint8_t *data; /* Head of data. */
- uint8_t *tail; /* End of data. */
- uint8_t *end; /* End of buffer. */
-
- struct skb_shared_info *shinfo;
-
/* FreeBSD specific bandaid (see linuxkpi_kfree_skb). */
void *m;
void(*m_free_func)(void *);
@@ -191,7 +209,7 @@ struct sk_buff *linuxkpi_dev_alloc_skb(size_t, gfp_t);
struct sk_buff *linuxkpi_build_skb(void *, size_t);
void linuxkpi_kfree_skb(struct sk_buff *);
-struct sk_buff *linuxkpi_skb_copy(struct sk_buff *, gfp_t);
+struct sk_buff *linuxkpi_skb_copy(const struct sk_buff *, gfp_t);
/* -------------------------------------------------------------------------- */
@@ -235,6 +253,13 @@ kfree_skb(struct sk_buff *skb)
}
static inline void
+consume_skb(struct sk_buff *skb)
+{
+ SKB_TRACE(skb);
+ kfree_skb(skb);
+}
+
+static inline void
dev_kfree_skb(struct sk_buff *skb)
{
SKB_TRACE(skb);
@@ -268,9 +293,12 @@ build_skb(void *data, unsigned int fragsz)
/* -------------------------------------------------------------------------- */
-/* XXX BZ review this one for terminal condition as Linux "queues" are special. */
-#define skb_list_walk_safe(_q, skb, tmp) \
- for ((skb) = (_q)->next; (skb) != NULL && ((tmp) = (skb)->next); (skb) = (tmp))
+static inline bool
+skb_is_nonlinear(struct sk_buff *skb)
+{
+ SKB_TRACE(skb);
+ return ((skb->data_len > 0) ? true : false);
+}
/* Add headroom; cannot do once there is data in there. */
static inline void
@@ -342,12 +370,14 @@ skb_tailroom(struct sk_buff *skb)
SKB_TRACE(skb);
KASSERT((skb->end - skb->tail) >= 0, ("%s: skb %p tailroom < 0, "
"end %p tail %p\n", __func__, skb, skb->end, skb->tail));
+ if (unlikely(skb_is_nonlinear(skb)))
+ return (0);
return (skb->end - skb->tail);
}
-/* Return numer of bytes available at the beginning of buffer. */
+/* Return number of bytes available at the beginning of buffer. */
static inline unsigned int
-skb_headroom(struct sk_buff *skb)
+skb_headroom(const struct sk_buff *skb)
{
SKB_TRACE(skb);
KASSERT((skb->data - skb->head) >= 0, ("%s: skb %p headroom < 0, "
@@ -494,13 +524,10 @@ skb_add_rx_frag(struct sk_buff *skb, int fragno, struct page *page,
skb->len += size;
skb->data_len += size;
skb->truesize += truesize;
-
- /* XXX TODO EXTEND truesize? */
}
/* -------------------------------------------------------------------------- */
-/* XXX BZ review this one for terminal condition as Linux "queues" are special. */
#define skb_queue_walk(_q, skb) \
for ((skb) = (_q)->next; (skb) != (struct sk_buff *)(_q); \
(skb) = (skb)->next)
@@ -509,12 +536,23 @@ skb_add_rx_frag(struct sk_buff *skb, int fragno, struct page *page,
for ((skb) = (_q)->next, (tmp) = (skb)->next; \
(skb) != (struct sk_buff *)(_q); (skb) = (tmp), (tmp) = (skb)->next)
+#define skb_list_walk_safe(_q, skb, tmp) \
+ for ((skb) = (_q), (tmp) = ((skb) != NULL) ? (skb)->next ? NULL; \
+ ((skb) != NULL); \
+ (skb) = (tmp), (tmp) = ((skb) != NULL) ? (skb)->next ? NULL)
+
static inline bool
-skb_queue_empty(struct sk_buff_head *q)
+skb_queue_empty(const struct sk_buff_head *q)
{
+ SKB_TRACE(q);
+ return (q->next == (const struct sk_buff *)q);
+}
+static inline bool
+skb_queue_empty_lockless(const struct sk_buff_head *q)
+{
SKB_TRACE(q);
- return (q->qlen == 0);
+ return (READ_ONCE(q->next) == (const struct sk_buff *)q);
}
static inline void
@@ -529,7 +567,8 @@ static inline void
skb_queue_head_init(struct sk_buff_head *q)
{
SKB_TRACE(q);
- return (__skb_queue_head_init(q));
+ __skb_queue_head_init(q);
+ spin_lock_init(&q->lock);
}
static inline void
@@ -538,11 +577,11 @@ __skb_insert(struct sk_buff *new, struct sk_buff *prev, struct sk_buff *next,
{
SKB_TRACE_FMT(new, "prev %p next %p q %p", prev, next, q);
- new->prev = prev;
- new->next = next;
- ((struct sk_buff_head_l *)next)->prev = new;
- ((struct sk_buff_head_l *)prev)->next = new;
- q->qlen++;
+ WRITE_ONCE(new->prev, prev);
+ WRITE_ONCE(new->next, next);
+ WRITE_ONCE(((struct sk_buff_head_l *)next)->prev, new);
+ WRITE_ONCE(((struct sk_buff_head_l *)prev)->next, new);
+ WRITE_ONCE(q->qlen, q->qlen + 1);
}
static inline void
@@ -574,53 +613,62 @@ __skb_queue_tail(struct sk_buff_head *q, struct sk_buff *new)
static inline void
skb_queue_tail(struct sk_buff_head *q, struct sk_buff *new)
{
+ unsigned long flags;
+
SKB_TRACE2(q, new);
- return (__skb_queue_tail(q, new));
+ spin_lock_irqsave(&q->lock, flags);
+ __skb_queue_tail(q, new);
+ spin_unlock_irqrestore(&q->lock, flags);
}
static inline struct sk_buff *
-skb_peek(struct sk_buff_head *q)
+skb_peek(const struct sk_buff_head *q)
{
struct sk_buff *skb;
skb = q->next;
SKB_TRACE2(q, skb);
- if (skb == (struct sk_buff *)q)
+ if (skb == (const struct sk_buff *)q)
return (NULL);
return (skb);
}
static inline struct sk_buff *
-skb_peek_tail(struct sk_buff_head *q)
+skb_peek_tail(const struct sk_buff_head *q)
{
struct sk_buff *skb;
- skb = q->prev;
+ skb = READ_ONCE(q->prev);
SKB_TRACE2(q, skb);
- if (skb == (struct sk_buff *)q)
+ if (skb == (const struct sk_buff *)q)
return (NULL);
return (skb);
}
static inline void
-__skb_unlink(struct sk_buff *skb, struct sk_buff_head *head)
+__skb_unlink(struct sk_buff *skb, struct sk_buff_head *q)
{
- SKB_TRACE2(skb, head);
- struct sk_buff *p, *n;;
+ struct sk_buff *p, *n;
+
+ SKB_TRACE2(skb, q);
- head->qlen--;
+ WRITE_ONCE(q->qlen, q->qlen - 1);
p = skb->prev;
n = skb->next;
- p->next = n;
- n->prev = p;
+ WRITE_ONCE(n->prev, p);
+ WRITE_ONCE(p->next, n);
skb->prev = skb->next = NULL;
}
static inline void
-skb_unlink(struct sk_buff *skb, struct sk_buff_head *head)
+skb_unlink(struct sk_buff *skb, struct sk_buff_head *q)
{
- SKB_TRACE2(skb, head);
- return (__skb_unlink(skb, head));
+ unsigned long flags;
+
+ SKB_TRACE2(skb, q);
+ spin_lock_irqsave(&q->lock, flags);
+ __skb_unlink(skb, q);
+ spin_unlock_irqrestore(&q->lock, flags);
}
static inline struct sk_buff *
@@ -628,32 +676,47 @@ __skb_dequeue(struct sk_buff_head *q)
{
struct sk_buff *skb;
- SKB_TRACE(q);
- skb = q->next;
- if (skb == (struct sk_buff *)q)
- return (NULL);
+ skb = skb_peek(q);
if (skb != NULL)
__skb_unlink(skb, q);
- SKB_TRACE(skb);
+ SKB_TRACE2(q, skb);
return (skb);
}
static inline struct sk_buff *
skb_dequeue(struct sk_buff_head *q)
{
- SKB_TRACE(q);
- return (__skb_dequeue(q));
+ unsigned long flags;
+ struct sk_buff *skb;
+
+ spin_lock_irqsave(&q->lock, flags);
+ skb = __skb_dequeue(q);
+ spin_unlock_irqrestore(&q->lock, flags);
+ SKB_TRACE2(q, skb);
+ return (skb);
}
static inline struct sk_buff *
-skb_dequeue_tail(struct sk_buff_head *q)
+__skb_dequeue_tail(struct sk_buff_head *q)
{
struct sk_buff *skb;
skb = skb_peek_tail(q);
if (skb != NULL)
__skb_unlink(skb, q);
+ SKB_TRACE2(q, skb);
+ return (skb);
+}
+static inline struct sk_buff *
+skb_dequeue_tail(struct sk_buff_head *q)
+{
+ unsigned long flags;
+ struct sk_buff *skb;
+
+ spin_lock_irqsave(&q->lock, flags);
+ skb = __skb_dequeue_tail(q);
+ spin_unlock_irqrestore(&q->lock, flags);
SKB_TRACE2(q, skb);
return (skb);
}
@@ -669,28 +732,75 @@ __skb_queue_head(struct sk_buff_head *q, struct sk_buff *skb)
static inline void
skb_queue_head(struct sk_buff_head *q, struct sk_buff *skb)
{
+ unsigned long flags;
SKB_TRACE2(q, skb);
- __skb_queue_after(q, (struct sk_buff *)q, skb);
+ spin_lock_irqsave(&q->lock, flags);
+ __skb_queue_head(q, skb);
+ spin_unlock_irqrestore(&q->lock, flags);
}
static inline uint32_t
-skb_queue_len(struct sk_buff_head *head)
+skb_queue_len(const struct sk_buff_head *q)
{
- SKB_TRACE(head);
- return (head->qlen);
+ SKB_TRACE(q);
+ return (q->qlen);
}
static inline uint32_t
-skb_queue_len_lockless(const struct sk_buff_head *head)
+skb_queue_len_lockless(const struct sk_buff_head *q)
{
- SKB_TRACE(head);
- return (READ_ONCE(head->qlen));
+ SKB_TRACE(q);
+ return (READ_ONCE(q->qlen));
}
static inline void
+___skb_queue_splice(const struct sk_buff_head *from,
+ struct sk_buff *p, struct sk_buff *n)
+{
+ struct sk_buff *b, *e;
+
+ b = from->next;
+ e = from->prev;
+
+ WRITE_ONCE(b->prev, p);
+ WRITE_ONCE(((struct sk_buff_head_l *)p)->next, b);
+ WRITE_ONCE(e->next, n);
+ WRITE_ONCE(((struct sk_buff_head_l *)n)->prev, e);
+}
+
+static inline void
+skb_queue_splice_init(struct sk_buff_head *from, struct sk_buff_head *to)
+{
+
+ SKB_TRACE2(from, to);
+
+ if (skb_queue_empty(from))
+ return;
+
+ ___skb_queue_splice(from, (struct sk_buff *)to, to->next);
+ to->qlen += from->qlen;
+ __skb_queue_head_init(from);
+}
+
+static inline void
+skb_queue_splice_tail_init(struct sk_buff_head *from, struct sk_buff_head *to)
+{
+
+ SKB_TRACE2(from, to);
+
+ if (skb_queue_empty(from))
+ return;
+
+ ___skb_queue_splice(from, to->prev, (struct sk_buff *)to);
+ to->qlen += from->qlen;
+ __skb_queue_head_init(from);
+}
+
+
+static inline void
__skb_queue_purge(struct sk_buff_head *q)
{
struct sk_buff *skb;
@@ -698,13 +808,26 @@ __skb_queue_purge(struct sk_buff_head *q)
SKB_TRACE(q);
while ((skb = __skb_dequeue(q)) != NULL)
kfree_skb(skb);
+ WARN_ONCE(skb_queue_len(q) != 0, "%s: queue %p not empty: %u",
+ __func__, q, skb_queue_len(q));
}
static inline void
skb_queue_purge(struct sk_buff_head *q)
{
+ struct sk_buff_head _q;
+ unsigned long flags;
+
SKB_TRACE(q);
- return (__skb_queue_purge(q));
+
+ if (skb_queue_empty_lockless(q))
+ return;
+
+ __skb_queue_head_init(&_q);
+ spin_lock_irqsave(&q->lock, flags);
+ skb_queue_splice_init(q, &_q);
+ spin_unlock_irqrestore(&q->lock, flags);
+ __skb_queue_purge(&_q);
}
static inline struct sk_buff *
@@ -719,7 +842,7 @@ skb_queue_prev(struct sk_buff_head *q, struct sk_buff *skb)
/* -------------------------------------------------------------------------- */
static inline struct sk_buff *
-skb_copy(struct sk_buff *skb, gfp_t gfp)
+skb_copy(const struct sk_buff *skb, gfp_t gfp)
{
struct sk_buff *new;
@@ -728,13 +851,6 @@ skb_copy(struct sk_buff *skb, gfp_t gfp)
return (new);
}
-static inline void
-consume_skb(struct sk_buff *skb)
-{
- SKB_TRACE(skb);
- SKB_TODO();
-}
-
static inline uint16_t
skb_checksum(struct sk_buff *skb, int offs, size_t len, int x)
{
@@ -764,8 +880,7 @@ static inline size_t
skb_frag_size(const skb_frag_t *frag)
{
SKB_TRACE(frag);
- SKB_TODO();
- return (-1);
+ return (frag->size);
}
#define skb_walk_frags(_skb, _frag) \
@@ -790,8 +905,7 @@ static inline void *
skb_frag_address(const skb_frag_t *frag)
{
SKB_TRACE(frag);
- SKB_TODO();
- return (NULL);
+ return (page_address(frag->page + frag->offset));
}
static inline void
@@ -821,50 +935,7 @@ static inline void
skb_mark_not_on_list(struct sk_buff *skb)
{
SKB_TRACE(skb);
- SKB_TODO();
-}
-
-static inline void
-___skb_queue_splice(const struct sk_buff_head *from,
- struct sk_buff *p, struct sk_buff *n)
-{
- struct sk_buff *b, *e;
-
- b = from->next;
- e = from->prev;
-
- b->prev = p;
- ((struct sk_buff_head_l *)p)->next = b;
- e->next = n;
- ((struct sk_buff_head_l *)n)->prev = e;
-}
-
-static inline void
-skb_queue_splice_init(struct sk_buff_head *from, struct sk_buff_head *to)
-{
-
- SKB_TRACE2(from, to);
-
- if (skb_queue_empty(from))
- return;
-
- ___skb_queue_splice(from, (struct sk_buff *)to, to->next);
- to->qlen += from->qlen;
- __skb_queue_head_init(from);
-}
-
-static inline void
-skb_queue_splice_tail_init(struct sk_buff_head *from, struct sk_buff_head *to)
-{
-
- SKB_TRACE2(from, to);
-
- if (skb_queue_empty(from))
- return;
-
- ___skb_queue_splice(from, to->prev, (struct sk_buff *)to);
- to->qlen += from->qlen;
- __skb_queue_head_init(from);
+ skb->next = NULL;
}
static inline void
@@ -891,25 +962,17 @@ skb_network_header(struct sk_buff *skb)
return (skb->head + skb->l3hdroff);
}
-static inline bool
-skb_is_nonlinear(struct sk_buff *skb)
-{
- SKB_TRACE(skb);
- return ((skb->data_len > 0) ? true : false);
-}
-
static inline int
__skb_linearize(struct sk_buff *skb)
{
SKB_TRACE(skb);
SKB_TODO();
- return (ENXIO);
+ return (-ENXIO);
}
static inline int
skb_linearize(struct sk_buff *skb)
{
-
return (skb_is_nonlinear(skb) ? __skb_linearize(skb) : 0);
}
@@ -938,12 +1001,19 @@ skb_get_queue_mapping(struct sk_buff *skb)
return (skb->qmap);
}
+static inline void
+skb_copy_header(struct sk_buff *to, const struct sk_buff *from)
+{
+ SKB_TRACE2(to, from);
+ SKB_TODO();
+}
+
static inline bool
skb_header_cloned(struct sk_buff *skb)
{
SKB_TRACE(skb);
SKB_TODO();
- return (false);
+ return (true);
}
static inline uint8_t *
@@ -983,10 +1053,9 @@ skb_orphan(struct sk_buff *skb)
SKB_TODO();
}
-static inline __sum16
+static inline __wsum
csum_unfold(__sum16 sum)
{
- SKB_TODO();
return (sum);
}
@@ -1012,7 +1081,8 @@ static inline struct sk_buff *
skb_get(struct sk_buff *skb)
{
- SKB_TODO(); /* XXX refcnt? as in get/put_device? */
+ SKB_TRACE(skb);
+ refcount_inc(&skb->refcnt);
return (skb);
}
@@ -1047,7 +1117,8 @@ skb_list_del_init(struct sk_buff *skb)
{
SKB_TRACE(skb);
- SKB_TODO();
+ __list_del_entry(&skb->list);
+ skb_mark_not_on_list(skb);
}
static inline void
@@ -1078,6 +1149,7 @@ static inline void
skb_mark_for_recycle(struct sk_buff *skb)
{
SKB_TRACE(skb);
+ /* page_pool */
SKB_TODO();
}
diff --git a/sys/compat/linuxkpi/common/include/linux/slab.h b/sys/compat/linuxkpi/common/include/linux/slab.h
index 298306b6ea05..f3a840d9bf4b 100644
--- a/sys/compat/linuxkpi/common/include/linux/slab.h
+++ b/sys/compat/linuxkpi/common/include/linux/slab.h
@@ -4,6 +4,10 @@
* Copyright (c) 2010 Panasas, Inc.
* Copyright (c) 2013-2021 Mellanox Technologies, Ltd.
* All rights reserved.
+ * Copyright (c) 2024-2025 The FreeBSD Foundation
+ *
+ * Portions of this software were developed by Björn Zeeb
+ * 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
@@ -41,13 +45,12 @@
MALLOC_DECLARE(M_KMALLOC);
-#define kmalloc(size, flags) lkpi_kmalloc(size, flags)
-#define kvmalloc(size, flags) kmalloc(size, flags)
#define kvzalloc(size, flags) kmalloc(size, (flags) | __GFP_ZERO)
#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 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)
#define vfree(arg) kfree(arg)
#define kvfree(arg) kfree(arg)
@@ -85,12 +88,22 @@ struct linux_kmem_cache;
#define ARCH_KMALLOC_MINALIGN \
__alignof(unsigned long long)
-/* drm-kmod 5.4 compat */
-#define kfree_async(ptr) kfree(ptr);
-
#define ZERO_SIZE_PTR ((void *)16)
#define ZERO_OR_NULL_PTR(x) ((x) == NULL || (x) == ZERO_SIZE_PTR)
+struct linux_kmem_cache *linux_kmem_cache_create(const char *name,
+ size_t size, size_t align, unsigned flags, linux_kmem_ctor_t *ctor);
+void *lkpi_kmem_cache_alloc(struct linux_kmem_cache *, gfp_t);
+void *lkpi_kmem_cache_zalloc(struct linux_kmem_cache *, gfp_t);
+void lkpi_kmem_cache_free(struct linux_kmem_cache *, void *);
+void linux_kmem_cache_destroy(struct linux_kmem_cache *);
+
+void *lkpi_kmalloc(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_kfree(const void *);
+
static inline gfp_t
linux_check_m_flags(gfp_t flags)
{
@@ -106,35 +119,89 @@ linux_check_m_flags(gfp_t flags)
return (flags & GFP_NATIVE_MASK);
}
+/*
+ * Base functions with a native implementation.
+ */
+static inline void *
+kmalloc(size_t size, gfp_t flags)
+{
+ return (lkpi_kmalloc(size, flags));
+}
+
static inline void *
__kmalloc(size_t size, gfp_t flags)
{
- return (malloc(MAX(size, sizeof(struct llist_node)), M_KMALLOC,
- linux_check_m_flags(flags)));
+ return (lkpi___kmalloc(size, flags));
}
static inline void *
kmalloc_node(size_t size, gfp_t flags, int node)
{
- return (malloc_domainset(size, M_KMALLOC,
- linux_get_vm_domain_set(node), linux_check_m_flags(flags)));
+ return (lkpi___kmalloc_node(size, flags, node));
+}
+
+static inline void *
+krealloc(void *ptr, size_t size, gfp_t flags)
+{
+ return (lkpi_krealloc(ptr, size, flags));
+}
+
+static inline void
+kfree(const void *ptr)
+{
+ lkpi_kfree(ptr);
+}
+
+/*
+ * Other k*alloc() funtions using the above as underlying allocator.
+ */
+/* kmalloc */
+static inline void *
+kmalloc_array(size_t n, size_t size, gfp_t flags)
+{
+ if (WOULD_OVERFLOW(n, size))
+ panic("%s: %zu * %zu overflowed", __func__, n, size);
+
+ return (kmalloc(size * n, flags));
}
static inline void *
kcalloc(size_t n, size_t size, gfp_t flags)
{
flags |= __GFP_ZERO;
- return (mallocarray(n, size, M_KMALLOC, linux_check_m_flags(flags)));
+ return (kmalloc_array(n, size, flags));
+}
+
+/* kmalloc_node */
+static inline void *
+kmalloc_array_node(size_t n, size_t size, gfp_t flags, int node)
+{
+ if (WOULD_OVERFLOW(n, size))
+ panic("%s: %zu * %zu overflowed", __func__, n, size);
+
+ return (kmalloc_node(size * n, flags, node));
}
static inline void *
kcalloc_node(size_t n, size_t size, gfp_t flags, int node)
{
flags |= __GFP_ZERO;
- return (mallocarray_domainset(n, size, M_KMALLOC,
- linux_get_vm_domain_set(node), linux_check_m_flags(flags)));
+ return (kmalloc_array_node(n, size, flags, node));
}
+/* krealloc */
+static inline void *
+krealloc_array(void *ptr, size_t n, size_t size, gfp_t flags)
+{
+ if (WOULD_OVERFLOW(n, size))
+ return NULL;
+
+ return (krealloc(ptr, n * size, flags));
+}
+
+/*
+ * vmalloc/kvalloc functions.
+ */
static inline void *
__vmalloc(size_t size, gfp_t flags, int other)
{
@@ -154,55 +221,43 @@ vmalloc_32(size_t size)
return (contigmalloc(size, M_KMALLOC, M_WAITOK, 0, UINT_MAX, 1, 1));
}
+/* May return non-contiguous memory. */
static inline void *
-kmalloc_array(size_t n, size_t size, gfp_t flags)
+kvmalloc(size_t size, gfp_t flags)
{
- return (mallocarray(n, size, M_KMALLOC, linux_check_m_flags(flags)));
-}
-
-static inline void *
-kmalloc_array_node(size_t n, size_t size, gfp_t flags, int node)
-{
- return (mallocarray_domainset(n, size, M_KMALLOC,
- linux_get_vm_domain_set(node), linux_check_m_flags(flags)));
+ return (malloc(size, M_KMALLOC, linux_check_m_flags(flags)));
}
static inline void *
kvmalloc_array(size_t n, size_t size, gfp_t flags)
{
- return (mallocarray(n, size, M_KMALLOC, linux_check_m_flags(flags)));
-}
+ if (WOULD_OVERFLOW(n, size))
+ panic("%s: %zu * %zu overflowed", __func__, n, size);
-static inline void *
-krealloc(void *ptr, size_t size, gfp_t flags)
-{
- return (realloc(ptr, size, M_KMALLOC, linux_check_m_flags(flags)));
+ return (kvmalloc(size * n, flags));
}
static inline void *
-krealloc_array(void *ptr, size_t n, size_t size, gfp_t flags)
+kvrealloc(const void *ptr, size_t oldsize, size_t newsize, gfp_t flags)
{
- if (WOULD_OVERFLOW(n, size)) {
- return NULL;
- }
-
- return (realloc(ptr, n * size, M_KMALLOC, linux_check_m_flags(flags)));
-}
+ void *newptr;
-extern void linux_kfree_async(void *);
+ if (newsize <= oldsize)
+ return (__DECONST(void *, ptr));
-static inline void
-kfree(const void *ptr)
-{
- if (ZERO_OR_NULL_PTR(ptr))
- return;
+ newptr = kvmalloc(newsize, flags);
+ if (newptr != NULL) {
+ memcpy(newptr, ptr, oldsize);
+ kvfree(ptr);
+ }
- if (curthread->td_critnest != 0)
- linux_kfree_async(__DECONST(void *, ptr));
- else
- free(__DECONST(void *, ptr), M_KMALLOC);
+ return (newptr);
}
+/*
+ * Misc.
+ */
+
static __inline void
kfree_sensitive(const void *ptr)
{
@@ -218,12 +273,12 @@ ksize(const void *ptr)
return (malloc_usable_size(ptr));
}
-extern void *lkpi_kmalloc(size_t size, gfp_t flags);
-extern struct linux_kmem_cache *linux_kmem_cache_create(const char *name,
- size_t size, size_t align, unsigned flags, linux_kmem_ctor_t *ctor);
-extern void *lkpi_kmem_cache_alloc(struct linux_kmem_cache *, gfp_t);
-extern void *lkpi_kmem_cache_zalloc(struct linux_kmem_cache *, gfp_t);
-extern void lkpi_kmem_cache_free(struct linux_kmem_cache *, void *);
-extern void linux_kmem_cache_destroy(struct linux_kmem_cache *);
+static inline size_t
+kmalloc_size_roundup(size_t size)
+{
+ if (unlikely(size == 0 || size == SIZE_MAX))
+ return (size);
+ return (malloc_size(size));
+}
#endif /* _LINUXKPI_LINUX_SLAB_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 b0aec2d4afbd..903053e7f6e8 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
@@ -42,6 +42,8 @@ struct mtk_wed_device {
#define mtk_wed_device_ppe_check(_dev, _skb, _reason, _entry) \
do {} while (0)
#define mtk_wed_device_stop(_dev) do { } while(0)
+#define mtk_wed_device_start_hw_rro(_dev, _mask, _b) do { } while(0)
+#define mtk_wed_device_setup_tc(_dev, _ndev, _type, _tdata) (-EOPNOTSUPP)
static inline bool
mtk_wed_device_active(struct mtk_wed_device *dev __unused)
diff --git a/sys/compat/linuxkpi/common/include/linux/spinlock.h b/sys/compat/linuxkpi/common/include/linux/spinlock.h
index 4bc1e2a2d431..dc10b0457153 100644
--- a/sys/compat/linuxkpi/common/include/linux/spinlock.h
+++ b/sys/compat/linuxkpi/common/include/linux/spinlock.h
@@ -41,9 +41,7 @@
#include <linux/bottom_half.h>
#include <linux/lockdep.h>
-typedef struct {
- struct mtx m;
-} spinlock_t;
+typedef struct mtx spinlock_t;
/*
* By defining CONFIG_SPIN_SKIP LinuxKPI spinlocks and asserts will be
@@ -59,7 +57,7 @@ typedef struct {
#define spin_lock(_l) do { \
if (SPIN_SKIP()) \
break; \
- mtx_lock(&(_l)->m); \
+ mtx_lock(_l); \
local_bh_disable(); \
} while (0)
@@ -76,7 +74,7 @@ typedef struct {
if (SPIN_SKIP()) \
break; \
local_bh_enable(); \
- mtx_unlock(&(_l)->m); \
+ mtx_unlock(_l); \
} while (0)
#define spin_unlock_bh(_l) do { \
@@ -93,7 +91,7 @@ typedef struct {
if (SPIN_SKIP()) { \
__ret = 1; \
} else { \
- __ret = mtx_trylock(&(_l)->m); \
+ __ret = mtx_trylock(_l); \
if (likely(__ret != 0)) \
local_bh_disable(); \
} \
@@ -111,7 +109,7 @@ typedef struct {
#define spin_lock_nested(_l, _n) do { \
if (SPIN_SKIP()) \
break; \
- mtx_lock_flags(&(_l)->m, MTX_DUPOK); \
+ mtx_lock_flags(_l, MTX_DUPOK); \
local_bh_disable(); \
} while (0)
@@ -141,31 +139,27 @@ typedef struct {
#define _spin_lock_name(...) __spin_lock_name(__VA_ARGS__)
#define spin_lock_name(name) _spin_lock_name(name, __FILE__, __LINE__)
-#define spin_lock_init(lock) linux_spin_lock_init(lock, spin_lock_name("lnxspin"))
+#define spin_lock_init(lock) mtx_init(lock, spin_lock_name("lnxspin"), \
+ NULL, MTX_DEF | MTX_NOWITNESS | MTX_NEW)
-static inline void
-linux_spin_lock_init(spinlock_t *lock, const char *name)
-{
-
- memset(lock, 0, sizeof(*lock));
- mtx_init(&lock->m, name, NULL, MTX_DEF | MTX_NOWITNESS);
-}
-
-static inline void
-spin_lock_destroy(spinlock_t *lock)
-{
-
- mtx_destroy(&lock->m);
-}
+#define spin_lock_destroy(_l) mtx_destroy(_l)
#define DEFINE_SPINLOCK(lock) \
spinlock_t lock; \
- MTX_SYSINIT(lock, &(lock).m, spin_lock_name("lnxspin"), MTX_DEF)
+ MTX_SYSINIT(lock, &lock, spin_lock_name("lnxspin"), MTX_DEF)
#define assert_spin_locked(_l) do { \
if (SPIN_SKIP()) \
break; \
- mtx_assert(&(_l)->m, MA_OWNED); \
+ mtx_assert(_l, MA_OWNED); \
+} while (0)
+
+#define local_irq_save(flags) do { \
+ (flags) = 0; \
+} while (0)
+
+#define local_irq_restore(flags) do { \
+ (void)(flags); \
} while (0)
#define atomic_dec_and_lock_irqsave(cnt, lock, flags) \
diff --git a/sys/compat/linuxkpi/common/include/linux/stdarg.h b/sys/compat/linuxkpi/common/include/linux/stdarg.h
index ab2fdf7534e5..698ac45e9198 100644
--- a/sys/compat/linuxkpi/common/include/linux/stdarg.h
+++ b/sys/compat/linuxkpi/common/include/linux/stdarg.h
@@ -28,6 +28,6 @@
#ifndef _LINUXKPI_STDARG_H_
#define _LINUXKPI_STDARG_H_
-#include <machine/stdarg.h>
+#include <sys/stdarg.h>
#endif /* _LINUXKPI_STDARG_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/stddef.h b/sys/compat/linuxkpi/common/include/linux/stddef.h
index a3bc6b13765e..d04a5a4bf516 100644
--- a/sys/compat/linuxkpi/common/include/linux/stddef.h
+++ b/sys/compat/linuxkpi/common/include/linux/stddef.h
@@ -5,11 +5,27 @@
#include <sys/stddef.h>
-#define struct_group(NAME, ...) \
+/*
+ * FreeBSD has multiple (vendor) drivers containing copies of this
+ * and including LinuxKPI headers. Put the #defines behind guards.
+ */
+
+#ifndef __struct_group
+#define __struct_group(_tag, _name, _attrs, _members...) \
union { \
- struct { __VA_ARGS__ }; \
- struct { __VA_ARGS__ } NAME; \
- }
+ struct { _members } _attrs; \
+ struct _tag { _members } _attrs _name; \
+ } _attrs
+#endif
-#endif /* _LINUXKPI_LINUX_STDDEF_H_ */
+#ifndef struct_group
+#define struct_group(_name, _members...) \
+ __struct_group(/* no tag */, _name, /* no attrs */, _members)
+#endif
+#ifndef struct_group_tagged
+#define struct_group_tagged(_tag, _name, _members...) \
+ __struct_group(_tag, _name, /* no attrs */, _members)
+#endif
+
+#endif /* _LINUXKPI_LINUX_STDDEF_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/string.h b/sys/compat/linuxkpi/common/include/linux/string.h
index 9e1818a38594..f7b64560d254 100644
--- a/sys/compat/linuxkpi/common/include/linux/string.h
+++ b/sys/compat/linuxkpi/common/include/linux/string.h
@@ -98,6 +98,18 @@ kmemdup(const void *src, size_t len, gfp_t gfp)
return (dst);
}
+/* See slab.h for kvmalloc/kvfree(). */
+static inline void *
+kvmemdup(const void *src, size_t len, gfp_t gfp)
+{
+ void *dst;
+
+ dst = kvmalloc(len, gfp);
+ if (dst != NULL)
+ memcpy(dst, src, len);
+ return (dst);
+}
+
static inline char *
strndup_user(const char __user *ustr, long n)
{
@@ -149,6 +161,24 @@ skip_spaces(const char *str)
return (__DECONST(char *, str));
}
+/*
+ * This function trims whitespaces at the end of a string and returns a pointer
+ * to the first non-whitespace character.
+ */
+static inline char *
+strim(char *str)
+{
+ char *end;
+
+ end = str + strlen(str);
+ while (end >= str && (*end == '\0' || isspace(*end))) {
+ *end = '\0';
+ end--;
+ }
+
+ return (skip_spaces(str));
+}
+
static inline void *
memchr_inv(const void *start, int c, size_t length)
{
diff --git a/sys/compat/linuxkpi/common/include/linux/swap.h b/sys/compat/linuxkpi/common/include/linux/swap.h
index a353d353cd33..5828db7ae392 100644
--- a/sys/compat/linuxkpi/common/include/linux/swap.h
+++ b/sys/compat/linuxkpi/common/include/linux/swap.h
@@ -37,6 +37,9 @@
#include <vm/swap_pager.h>
#include <vm/vm_pageout.h>
+#include <linux/pagemap.h>
+#include <linux/page-flags.h>
+
static inline long
get_nr_swap_pages(void)
{
@@ -54,4 +57,15 @@ current_is_kswapd(void)
return (curproc == pageproc);
}
+static inline void
+folio_mark_accessed(struct folio *folio)
+{
+ mark_page_accessed(&folio->page);
+}
+
+static inline void
+check_move_unevictable_folios(struct folio_batch *fbatch)
+{
+}
+
#endif
diff --git a/sys/compat/linuxkpi/common/include/linux/sysfs.h b/sys/compat/linuxkpi/common/include/linux/sysfs.h
index b5ad73e4460b..65e023031bb2 100644
--- a/sys/compat/linuxkpi/common/include/linux/sysfs.h
+++ b/sys/compat/linuxkpi/common/include/linux/sysfs.h
@@ -50,6 +50,15 @@ struct attribute_group {
struct attribute **attrs;
};
+struct bin_attribute {
+ struct attribute attr;
+ size_t size;
+ ssize_t (*read)(struct linux_file *, struct kobject *,
+ struct bin_attribute *, char *, loff_t, size_t);
+ ssize_t (*write)(struct linux_file *, struct kobject *,
+ struct bin_attribute *, char *, loff_t, size_t);
+};
+
#define __ATTR(_name, _mode, _show, _store) { \
.attr = { .name = __stringify(_name), .mode = _mode }, \
.show = _show, .store = _store, \
@@ -72,6 +81,39 @@ struct attribute_group {
NULL, \
}
+#define __BIN_ATTR(_name, _mode, _read, _write, _size) { \
+ .attr = { .name = __stringify(_name), .mode = _mode }, \
+ .read = _read, .write = _write, .size = _size, \
+}
+#define __BIN_ATTR_RO(_name, _size) { \
+ .attr = { .name = __stringify(_name), .mode = 0444 }, \
+ .read = _name##_read, .size = _size, \
+}
+#define __BIN_ATTR_WO(_name, _size) { \
+ .attr = { .name = __stringify(_name), .mode = 0200 }, \
+ .write = _name##_write, .size = _size, \
+}
+#define __BIN_ATTR_WR(_name, _size) { \
+ .attr = { .name = __stringify(_name), .mode = 0644 }, \
+ .read = _name##_read, .write = _name##_write, .size = _size, \
+}
+
+#define BIN_ATTR(_name, _mode, _read, _write, _size) \
+struct bin_attribute bin_attr_##_name = \
+ __BIN_ATTR(_name, _mode, _read, _write, _size);
+
+#define BIN_ATTR_RO(_name, _size) \
+struct bin_attribute bin_attr_##_name = \
+ __BIN_ATTR_RO(_name, _size);
+
+#define BIN_ATTR_WO(_name, _size) \
+struct bin_attribute bin_attr_##_name = \
+ __BIN_ATTR_WO(_name, _size);
+
+#define BIN_ATTR_WR(_name, _size) \
+struct bin_attribute bin_attr_##_name = \
+ __BIN_ATTR_WR(_name, _size);
+
/*
* Handle our generic '\0' terminated 'C' string.
* Two cases:
@@ -156,6 +198,80 @@ sysfs_remove_file(struct kobject *kobj, const struct attribute *attr)
}
static inline int
+sysctl_handle_bin_attr(SYSCTL_HANDLER_ARGS)
+{
+ struct kobject *kobj;
+ struct bin_attribute *attr;
+ char *buf;
+ int error;
+ ssize_t len;
+
+ kobj = arg1;
+ attr = (struct bin_attribute *)(intptr_t)arg2;
+ if (kobj->ktype == NULL || kobj->ktype->sysfs_ops == NULL)
+ return (ENODEV);
+ buf = (char *)get_zeroed_page(GFP_KERNEL);
+ if (buf == NULL)
+ return (ENOMEM);
+
+ if (attr->read) {
+ len = attr->read(
+ NULL, /* <-- struct file, unimplemented */
+ kobj, attr, buf, req->oldidx, PAGE_SIZE);
+ if (len < 0) {
+ error = -len;
+ if (error != EIO)
+ goto out;
+ }
+ }
+
+ error = sysctl_handle_opaque(oidp, buf, PAGE_SIZE, req);
+ if (error != 0 || req->newptr == NULL || attr->write == NULL)
+ goto out;
+
+ len = attr->write(
+ NULL, /* <-- struct file, unimplemented */
+ kobj, attr, buf, req->newidx, req->newlen);
+ if (len < 0)
+ error = -len;
+out:
+ free_page((unsigned long)buf);
+
+ return (error);
+}
+
+static inline int
+sysfs_create_bin_file(struct kobject *kobj, const struct bin_attribute *attr)
+{
+ struct sysctl_oid *oid;
+ int ctlflags;
+
+ ctlflags = CTLTYPE_OPAQUE | CTLFLAG_MPSAFE;
+ if (attr->attr.mode & (S_IRUSR | S_IWUSR))
+ ctlflags |= CTLFLAG_RW;
+ else if (attr->attr.mode & S_IRUSR)
+ ctlflags |= CTLFLAG_RD;
+ else if (attr->attr.mode & S_IWUSR)
+ ctlflags |= CTLFLAG_WR;
+
+ oid = SYSCTL_ADD_OID(NULL, SYSCTL_CHILDREN(kobj->oidp), OID_AUTO,
+ attr->attr.name, ctlflags, kobj,
+ (uintptr_t)attr, sysctl_handle_bin_attr, "", "");
+ if (oid == NULL)
+ return (-ENOMEM);
+
+ return (0);
+}
+
+static inline void
+sysfs_remove_bin_file(struct kobject *kobj, const struct bin_attribute *attr)
+{
+
+ if (kobj->oidp)
+ sysctl_remove_name(kobj->oidp, attr->attr.name, 1, 1);
+}
+
+static inline int
sysfs_create_link(struct kobject *kobj __unused,
struct kobject *target __unused, const char *name __unused)
{
@@ -348,6 +464,24 @@ sysfs_emit_at(char *buf, int at, const char *fmt, ...)
return (i);
}
+static inline int
+_sysfs_match_string(const char * const *a, size_t l, const char *s)
+{
+ const char *p;
+ int i;
+
+ for (i = 0; i < l; i++) {
+ p = a[i];
+ if (p == NULL)
+ break;
+ if (sysfs_streq(p, s))
+ return (i);
+ }
+
+ return (-ENOENT);
+}
+#define sysfs_match_string(a, s) _sysfs_match_string(a, ARRAY_SIZE(a), s)
+
#define sysfs_attr_init(attr) do {} while(0)
#endif /* _LINUXKPI_LINUX_SYSFS_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/time.h b/sys/compat/linuxkpi/common/include/linux/time.h
index 6c9a781d5e0e..ca77a20516ff 100644
--- a/sys/compat/linuxkpi/common/include/linux/time.h
+++ b/sys/compat/linuxkpi/common/include/linux/time.h
@@ -42,6 +42,8 @@
#include <linux/math64.h>
+typedef int64_t time64_t;
+
static inline struct timeval
ns_to_timeval(const int64_t nsec)
{
diff --git a/sys/compat/linuxkpi/common/include/linux/timer.h b/sys/compat/linuxkpi/common/include/linux/timer.h
index 8bea082c3e6c..a635f0faea59 100644
--- a/sys/compat/linuxkpi/common/include/linux/timer.h
+++ b/sys/compat/linuxkpi/common/include/linux/timer.h
@@ -42,7 +42,7 @@ struct timer_list {
void (*function_415) (struct timer_list *);
};
unsigned long data;
- int expires;
+ unsigned long expires;
};
extern unsigned long linux_timer_hz_mask;
@@ -76,7 +76,7 @@ extern unsigned long linux_timer_hz_mask;
callout_init(&(timer)->callout, 1); \
} while (0)
-extern int mod_timer(struct timer_list *, int);
+extern int mod_timer(struct timer_list *, unsigned long);
extern void add_timer(struct timer_list *);
extern void add_timer_on(struct timer_list *, int cpu);
extern int del_timer(struct timer_list *);
@@ -86,7 +86,7 @@ extern int timer_shutdown_sync(struct timer_list *);
#define timer_pending(timer) callout_pending(&(timer)->callout)
#define round_jiffies(j) \
- ((int)(((j) + linux_timer_hz_mask) & ~linux_timer_hz_mask))
+ ((unsigned long)(((j) + linux_timer_hz_mask) & ~linux_timer_hz_mask))
#define round_jiffies_relative(j) round_jiffies(j)
#define round_jiffies_up(j) round_jiffies(j)
#define round_jiffies_up_relative(j) round_jiffies_up(j)
diff --git a/sys/compat/linuxkpi/common/include/linux/types.h b/sys/compat/linuxkpi/common/include/linux/types.h
index b11ce1cdd805..fcc455e5f731 100644
--- a/sys/compat/linuxkpi/common/include/linux/types.h
+++ b/sys/compat/linuxkpi/common/include/linux/types.h
@@ -63,6 +63,7 @@ typedef unsigned gfp_t;
typedef off_t loff_t;
typedef vm_paddr_t resource_size_t;
typedef uint16_t __bitwise__ __sum16;
+typedef uint32_t __bitwise__ __wsum;
typedef unsigned long pgoff_t;
typedef unsigned __poll_t;
diff --git a/sys/compat/linuxkpi/common/include/linux/units.h b/sys/compat/linuxkpi/common/include/linux/units.h
new file mode 100644
index 000000000000..304b5c27d87f
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/units.h
@@ -0,0 +1,40 @@
+/*-
+ * 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_UNITS_H_
+#define _LINUXKPI_LINUX_UNITS_H_
+
+#define NANOHZ_PER_HZ 1000000000UL
+#define MICROHZ_PER_HZ 1000000UL
+#define MILLIHZ_PER_HZ 1000UL
+#define HZ_PER_KHZ 1000UL
+#define KHZ_PER_MHZ 1000UL
+#define HZ_PER_MHZ 1000000UL
+
+#endif /* _LINUXKPI_LINUX_UNITS_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/wait.h b/sys/compat/linuxkpi/common/include/linux/wait.h
index d50003d44955..03ddce2c06f5 100644
--- a/sys/compat/linuxkpi/common/include/linux/wait.h
+++ b/sys/compat/linuxkpi/common/include/linux/wait.h
@@ -61,12 +61,14 @@ typedef struct wait_queue_head wait_queue_head_t;
typedef int wait_queue_func_t(wait_queue_t *, unsigned int, int, void *);
+#define WQ_FLAG_WOKEN 0x02
+
/*
* Many API consumers directly reference these fields and those of
* wait_queue_head.
*/
struct wait_queue {
- unsigned int flags; /* always 0 */
+ unsigned int flags;
void *private;
wait_queue_func_t *func;
union {
@@ -87,8 +89,14 @@ struct wait_queue_head {
* This function is referenced by at least one DRM driver, so it may not be
* renamed and furthermore must be the default wait queue callback.
*/
-extern wait_queue_func_t autoremove_wake_function;
-extern wait_queue_func_t default_wake_function;
+wait_queue_func_t autoremove_wake_function;
+wait_queue_func_t default_wake_function;
+wait_queue_func_t woken_wake_function;
+
+long linux_wait_woken(wait_queue_t *wq, unsigned state, long timeout);
+
+#define wait_woken(wq, state, timeout) \
+ linux_wait_woken((wq), (state), (timeout))
#define DEFINE_WAIT_FUNC(name, function) \
wait_queue_t name = { \
@@ -110,10 +118,10 @@ extern wait_queue_func_t default_wake_function;
wait_queue_head_t name = { \
.task_list = LINUX_LIST_HEAD_INIT(name.task_list), \
}; \
- MTX_SYSINIT(name, &(name).lock.m, spin_lock_name("wqhead"), MTX_DEF)
+ MTX_SYSINIT(name, &(name).lock, spin_lock_name("wqhead"), MTX_DEF)
#define init_waitqueue_head(wqh) do { \
- mtx_init(&(wqh)->lock.m, spin_lock_name("wqhead"), \
+ mtx_init(&(wqh)->lock, spin_lock_name("wqhead"), \
NULL, MTX_DEF | MTX_NEW | MTX_NOWITNESS); \
INIT_LIST_HEAD(&(wqh)->task_list); \
} while (0)
@@ -138,7 +146,7 @@ void linux_wake_up(wait_queue_head_t *, unsigned int, int, bool);
#define wake_up_interruptible_all(wqh) \
linux_wake_up(wqh, TASK_INTERRUPTIBLE, 0, false)
-int linux_wait_event_common(wait_queue_head_t *, wait_queue_t *, int,
+int linux_wait_event_common(wait_queue_head_t *, wait_queue_t *, long,
unsigned int, spinlock_t *);
/*
@@ -148,9 +156,9 @@ int linux_wait_event_common(wait_queue_head_t *, wait_queue_t *, int,
*/
#define __wait_event_common(wqh, cond, timeout, state, lock) ({ \
DEFINE_WAIT(__wq); \
- const int __timeout = ((int)(timeout)) < 1 ? 1 : (timeout); \
- int __start = ticks; \
- int __ret = 0; \
+ const long __timeout = ((long)(timeout)) < 1 ? 1 : (timeout); \
+ long __start = jiffies; \
+ long __ret = 0; \
\
for (;;) { \
linux_prepare_to_wait(&(wqh), &__wq, state); \
@@ -166,7 +174,7 @@ int linux_wait_event_common(wait_queue_head_t *, wait_queue_t *, int,
if (__ret == -EWOULDBLOCK) \
__ret = !!(cond); \
else if (__ret != -ERESTARTSYS) { \
- __ret = __timeout + __start - ticks; \
+ __ret = __timeout + __start - jiffies; \
/* range check return value */ \
if (__ret < 1) \
__ret = 1; \
@@ -284,7 +292,7 @@ void linux_finish_wait(wait_queue_head_t *, wait_queue_t *);
#define finish_wait(wqh, wq) linux_finish_wait(wqh, wq)
void linux_wake_up_bit(void *, int);
-int linux_wait_on_bit_timeout(unsigned long *, int, unsigned int, int);
+int linux_wait_on_bit_timeout(unsigned long *, int, unsigned int, long);
void linux_wake_up_atomic_t(atomic_t *);
int linux_wait_on_atomic_t(atomic_t *, unsigned int);
diff --git a/sys/compat/linuxkpi/common/include/linux/workqueue.h b/sys/compat/linuxkpi/common/include/linux/workqueue.h
index 1c9df9fcb74d..66d3981d4229 100644
--- a/sys/compat/linuxkpi/common/include/linux/workqueue.h
+++ b/sys/compat/linuxkpi/common/include/linux/workqueue.h
@@ -90,7 +90,7 @@ struct delayed_work {
struct {
struct callout callout;
struct mtx mtx;
- int expires;
+ unsigned long expires;
} timer;
};
@@ -245,7 +245,7 @@ extern struct workqueue_struct *linux_create_workqueue_common(const char *, int)
extern void linux_destroy_workqueue(struct workqueue_struct *);
extern bool linux_queue_work_on(int cpu, struct workqueue_struct *, struct work_struct *);
extern bool linux_queue_delayed_work_on(int cpu, struct workqueue_struct *,
- struct delayed_work *, unsigned delay);
+ struct delayed_work *, unsigned long delay);
extern bool linux_cancel_work(struct work_struct *);
extern bool linux_cancel_delayed_work(struct delayed_work *);
extern bool linux_cancel_work_sync(struct work_struct *);
@@ -258,4 +258,10 @@ extern struct work_struct *linux_current_work(void);
extern bool linux_queue_rcu_work(struct workqueue_struct *wq, struct rcu_work *rwork);
extern bool linux_flush_rcu_work(struct rcu_work *rwork);
+static inline bool
+queue_work_node(int node __unused, struct workqueue_struct *wq, struct work_struct *work)
+{
+ return (queue_work(wq, work));
+}
+
#endif /* _LINUXKPI_LINUX_WORKQUEUE_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/xarray.h b/sys/compat/linuxkpi/common/include/linux/xarray.h
index d293a8f7c2a3..fba36eea0ab5 100644
--- a/sys/compat/linuxkpi/common/include/linux/xarray.h
+++ b/sys/compat/linuxkpi/common/include/linux/xarray.h
@@ -49,14 +49,14 @@
#define xa_limit_32b XA_LIMIT(0, 0xFFFFFFFF)
-#define XA_ASSERT_LOCKED(xa) mtx_assert(&(xa)->mtx, MA_OWNED)
-#define xa_lock(xa) mtx_lock(&(xa)->mtx)
-#define xa_unlock(xa) mtx_unlock(&(xa)->mtx)
+#define XA_ASSERT_LOCKED(xa) mtx_assert(&(xa)->xa_lock, MA_OWNED)
+#define xa_lock(xa) mtx_lock(&(xa)->xa_lock)
+#define xa_unlock(xa) mtx_unlock(&(xa)->xa_lock)
struct xarray {
- struct radix_tree_root root;
- struct mtx mtx; /* internal mutex */
- uint32_t flags; /* see XA_FLAGS_XXX */
+ struct radix_tree_root xa_head;
+ struct mtx xa_lock; /* internal mutex */
+ uint32_t xa_flags; /* see XA_FLAGS_XXX */
};
/*
@@ -67,6 +67,7 @@ void *xa_erase(struct xarray *, uint32_t);
void *xa_load(struct xarray *, uint32_t);
int xa_alloc(struct xarray *, uint32_t *, void *, uint32_t, gfp_t);
int xa_alloc_cyclic(struct xarray *, uint32_t *, void *, uint32_t, uint32_t *, gfp_t);
+int xa_alloc_cyclic_irq(struct xarray *, uint32_t *, void *, uint32_t, uint32_t *, gfp_t);
int xa_insert(struct xarray *, uint32_t, void *, gfp_t);
void *xa_store(struct xarray *, uint32_t, void *, gfp_t);
void xa_init_flags(struct xarray *, uint32_t);
diff --git a/sys/compat/linuxkpi/common/include/net/cfg80211.h b/sys/compat/linuxkpi/common/include/net/cfg80211.h
index 7e57ce67cc26..18b34f0e90ec 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-2023 The FreeBSD Foundation
+ * Copyright (c) 2020-2025 The FreeBSD Foundation
* Copyright (c) 2021-2022 Bjoern A. Zeeb
*
* This software was developed by Björn Zeeb under sponsorship from
@@ -33,14 +33,19 @@
#include <linux/types.h>
#include <linux/nl80211.h>
#include <linux/ieee80211.h>
+#include <linux/mutex.h>
#include <linux/if_ether.h>
#include <linux/ethtool.h>
#include <linux/device.h>
#include <linux/netdevice.h>
#include <linux/random.h>
#include <linux/skbuff.h>
+#include <linux/timer.h>
+#include <linux/workqueue.h>
#include <net/regulatory.h>
+#include <net80211/ieee80211.h>
+
/* linux_80211.c */
extern int linuxkpi_debug_80211;
#ifndef D80211_TODO
@@ -49,8 +54,8 @@ extern int linuxkpi_debug_80211;
#ifndef D80211_IMPROVE
#define D80211_IMPROVE 0x2
#endif
-#define TODO(...) if (linuxkpi_debug_80211 & D80211_TODO) \
- printf("%s:%d: XXX LKPI80211 TODO\n", __func__, __LINE__)
+#define TODO(fmt, ...) if (linuxkpi_debug_80211 & D80211_TODO) \
+ printf("%s:%d: XXX LKPI80211 TODO " fmt "\n", __func__, __LINE__, ##__VA_ARGS__)
#define IMPROVE(...) if (linuxkpi_debug_80211 & D80211_IMPROVE) \
printf("%s:%d: XXX LKPI80211 IMPROVE\n", __func__, __LINE__)
@@ -78,6 +83,9 @@ enum cfg80211_rate_info_flags {
/* Max 8 bits as used in struct rate_info. */
};
+#define CFG80211_RATE_INFO_FLAGS_BITS \
+ "\20\1MCS\2VHT_MCS\3SGI\5HE_MCS\10EHT_MCS"
+
extern const uint8_t rfc1042_header[6];
extern const uint8_t bridge_tunnel_header[6];
@@ -106,6 +114,11 @@ enum ieee80211_channel_flags {
IEEE80211_CHAN_NO_80MHZ = BIT(7),
IEEE80211_CHAN_NO_160MHZ = BIT(8),
IEEE80211_CHAN_NO_OFDM = BIT(9),
+ IEEE80211_CHAN_NO_6GHZ_VLP_CLIENT = BIT(10),
+ IEEE80211_CHAN_NO_6GHZ_AFC_CLIENT = BIT(11),
+ IEEE80211_CHAN_PSD = BIT(12),
+ IEEE80211_CHAN_ALLOW_6GHZ_VLP_AP = BIT(13),
+ IEEE80211_CHAN_CAN_MONITOR = BIT(14),
};
#define IEEE80211_CHAN_NO_HT40 (IEEE80211_CHAN_NO_HT40MINUS|IEEE80211_CHAN_NO_HT40PLUS)
@@ -156,7 +169,7 @@ enum rate_info_bw {
struct rate_info {
uint8_t flags; /* enum cfg80211_rate_info_flags */
- uint8_t bw;
+ uint8_t bw; /* enum rate_info_bw */
uint16_t legacy;
uint8_t mcs;
uint8_t nss;
@@ -167,11 +180,10 @@ struct rate_info {
};
struct ieee80211_rate {
- /* TODO FIXME */
- uint32_t bitrate;
- uint32_t hw_value;
- uint32_t hw_value_short;
- uint32_t flags;
+ uint32_t flags; /* enum ieee80211_rate_flags */
+ uint16_t bitrate;
+ uint16_t hw_value;
+ uint16_t hw_value_short;
};
struct ieee80211_sta_ht_cap {
@@ -190,7 +202,7 @@ struct ieee80211_sta_ht_cap {
#define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ (IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_160MHZ << IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_MASK_S)
#define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ (IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_160_80P80MHZ << IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_MASK_S)
-#define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK 0x0000000c /* IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_MASK */
+#define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_MASK
#define IEEE80211_VHT_CAP_RXLDPC 0x00000010 /* IEEE80211_VHTCAP_RXLDPC */
@@ -228,7 +240,8 @@ struct ieee80211_sta_ht_cap {
#define IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK \
(7 << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT) /* IEEE80211_VHTCAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK */
-#define IEEE80211_VHT_CAP_EXT_NSS_BW_MASK 0xc0000000
+#define IEEE80211_VHT_CAP_EXT_NSS_BW_MASK IEEE80211_VHTCAP_EXT_NSS_BW
+#define IEEE80211_VHT_CAP_EXT_NSS_BW_SHIFT IEEE80211_VHTCAP_EXT_NSS_BW_S
struct ieee80211_sta_vht_cap {
/* TODO FIXME */
@@ -275,6 +288,9 @@ struct cfg80211_bss_ies {
struct cfg80211_bss {
/* XXX TODO */
struct cfg80211_bss_ies *ies;
+ struct cfg80211_bss_ies *beacon_ies;
+
+ int32_t signal;
};
struct cfg80211_chan_def {
@@ -283,6 +299,7 @@ struct cfg80211_chan_def {
enum nl80211_chan_width width;
uint32_t center_freq1;
uint32_t center_freq2;
+ uint16_t punctured;
};
struct cfg80211_ftm_responder_stats {
@@ -378,11 +395,16 @@ struct cfg80211_match_set {
struct cfg80211_scan_request {
/* XXX TODO */
- int duration, duration_mandatory, flags;
bool no_cck;
bool scan_6ghz;
+ bool duration_mandatory;
+ int8_t tsf_report_link_id;
+ uint16_t duration;
+ uint32_t flags;
struct wireless_dev *wdev;
struct wiphy *wiphy;
+ uint64_t scan_start;
+ uint32_t rates[NUM_NL80211_BANDS];
int ie_len;
uint8_t *ie;
uint8_t mac_addr[ETH_ALEN], mac_addr_mask[ETH_ALEN];
@@ -529,18 +551,39 @@ struct station_del_parameters {
};
struct station_info {
- /* TODO FIXME */
- int assoc_req_ies_len, connected_time;
- int generation, inactive_time, rx_bytes, rx_dropped_misc, rx_packets, signal, tx_bytes, tx_packets;
- int filled, rx_beacon, rx_beacon_signal_avg, signal_avg;
- int rx_duration, tx_duration, tx_failed, tx_retries;
- int ack_signal, avg_ack_signal;
+ uint64_t filled; /* enum nl80211_sta_info */
+ uint32_t connected_time;
+ uint32_t inactive_time;
+
+ uint64_t rx_bytes;
+ uint32_t rx_packets;
+ uint32_t rx_dropped_misc;
+
+ uint64_t rx_duration;
+ uint32_t rx_beacon;
+ uint8_t rx_beacon_signal_avg;
+
+ int8_t signal;
+ int8_t signal_avg;
+ int8_t ack_signal;
+ int8_t avg_ack_signal;
+
+ /* gap */
+ int generation;
+
+ uint64_t tx_bytes;
+ uint32_t tx_packets;
+ uint32_t tx_failed;
+ uint64_t tx_duration;
+ uint32_t tx_retries;
int chains;
uint8_t chain_signal[IEEE80211_MAX_CHAINS];
uint8_t chain_signal_avg[IEEE80211_MAX_CHAINS];
uint8_t *assoc_req_ies;
+ size_t assoc_req_ies_len;
+
struct rate_info rxrate;
struct rate_info txrate;
struct cfg80211_ibss_params bss_param;
@@ -599,174 +642,6 @@ struct linuxkpi_ieee80211_regdomain {
struct ieee80211_reg_rule reg_rules[];
};
-/* XXX-BZ this are insensible values probably ... */
-#define IEEE80211_HE_MAC_CAP0_HTC_HE 0x1
-#define IEEE80211_HE_MAC_CAP0_TWT_REQ 0x2
-#define IEEE80211_HE_MAC_CAP0_TWT_RES 0x4
-
-#define IEEE80211_HE_MAC_CAP1_LINK_ADAPTATION 0x1
-#define IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8 0x2
-#define IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US 0x4
-#define IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_MASK 0x8
-
-#define IEEE80211_HE_MAC_CAP2_32BIT_BA_BITMAP 0x1
-#define IEEE80211_HE_MAC_CAP2_ACK_EN 0x2
-#define IEEE80211_HE_MAC_CAP2_BSR 0x4
-#define IEEE80211_HE_MAC_CAP2_LINK_ADAPTATION 0x8
-#define IEEE80211_HE_MAC_CAP2_BCAST_TWT 0x10
-#define IEEE80211_HE_MAC_CAP2_ALL_ACK 0x20
-#define IEEE80211_HE_MAC_CAP2_MU_CASCADING 0x40
-#define IEEE80211_HE_MAC_CAP2_TRS 0x80
-
-#define IEEE80211_HE_MAC_CAP3_OMI_CONTROL 0x02
-#define IEEE80211_HE_MAC_CAP3_OFDMA_RA 0x04
-#define IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_1 0x08
-#define IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_2 0x10
-#define IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3 0x18
-#define IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_MASK 0x18
-#define IEEE80211_HE_MAC_CAP3_FLEX_TWT_SCHED 0x40
-#define IEEE80211_HE_MAC_CAP3_RX_CTRL_FRAME_TO_MULTIBSS 0x80
-
-#define IEEE80211_HE_MAC_CAP4_AMDSU_IN_AMPDU 0x1
-#define IEEE80211_HE_MAC_CAP4_BQR 0x2
-#define IEEE80211_HE_MAC_CAP4_MULTI_TID_AGG_TX_QOS_B39 0x4
-#define IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU 0x8
-#define IEEE80211_HE_MAC_CAP4_OPS 0x10
-#define IEEE80211_HE_MAC_CAP4_BSRP_BQRP_A_MPDU_AGG 0x20
-
-#define IEEE80211_HE_MAC_CAP5_HE_DYNAMIC_SM_PS 0x1
-#define IEEE80211_HE_MAC_CAP5_HT_VHT_TRIG_FRAME_RX 0x2
-#define IEEE80211_HE_MAC_CAP5_MULTI_TID_AGG_TX_QOS_B40 0x4
-#define IEEE80211_HE_MAC_CAP5_MULTI_TID_AGG_TX_QOS_B41 0x8
-#define IEEE80211_HE_MAC_CAP5_UL_2x996_TONE_RU 0x10
-#define IEEE80211_HE_MAC_CAP5_OM_CTRL_UL_MU_DATA_DIS_RX 0x20
-#define IEEE80211_HE_MAC_CAP5_PUNCTURED_SOUNDING 0x40
-#define IEEE80211_HE_MAC_CAP5_SUBCHAN_SELECTIVE_TRANSMISSION 0x80
-
-#define IEEE80211_HE_MCS_NOT_SUPPORTED 0x0
-#define IEEE80211_HE_MCS_SUPPORT_0_7 0x1
-#define IEEE80211_HE_MCS_SUPPORT_0_9 0x2
-#define IEEE80211_HE_MCS_SUPPORT_0_11 0x4
-
-#define IEEE80211_HE_6GHZ_CAP_TX_ANTPAT_CONS 0x01
-#define IEEE80211_HE_6GHZ_CAP_RX_ANTPAT_CONS 0x02
-#define IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START 0x04
-#define IEEE80211_HE_6GHZ_CAP_MAX_MPDU_LEN 0x08
-#define IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP 0x10
-#define IEEE80211_HE_6GHZ_CAP_SM_PS 0x20
-
-#define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G 0x1
-#define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G 0x2
-#define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G 0x4
-#define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G 0x8
-#define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_2G 0x10
-#define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_5G 0x20
-#define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK 0x40
-#define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL 0xff
-
-#define IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A 0x1
-#define IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD 0x2
-#define IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS 0x4
-#define IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK 0x8
-#define IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US 0x10
-
-#define IEEE80211_HE_PHY_CAP2_MIDAMBLE_RX_TX_MAX_NSTS 0x1
-#define IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US 0x2
-#define IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ 0x4
-#define IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ 0x8
-#define IEEE80211_HE_PHY_CAP2_DOPPLER_TX 0x10
-#define IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO 0x20
-#define IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO 0x40
-
-#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_MASK 0x1
-#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_NO_DCM 0x2
-#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_NO_DCM 0x4
-#define IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1 0x8
-#define IEEE80211_HE_PHY_CAP3_DCM_MAX_TX_NSS_1 0x10
-#define IEEE80211_HE_PHY_CAP3_SU_BEAMFORMER 0x20
-#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_16_QAM 0x40
-#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_16_QAM 0x80
-#define IEEE80211_HE_PHY_CAP3_DCM_MAX_TX_NSS_2 0x10
-#define IEEE80211_HE_PHY_CAP3_RX_PARTIAL_BW_SU_IN_20MHZ_MU 0x20
-#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_BPSK 0x40
-#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_BPSK 0x80
-#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_MASK 0x80
-#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_QPSK 0x80
-#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_QPSK 0x80
-#define IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_2 0x80
-
-#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_8 0x1
-#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_8 0x2
-#define IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE 0x4
-#define IEEE80211_HE_PHY_CAP4_MU_BEAMFORMER 0x8
-#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4 0x10
-#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_4 0x20
-#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_MASK 0x40
-#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_MASK 0x80
-
-#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_2 0x1
-#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_2 0x2
-#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK 0x4
-#define IEEE80211_HE_PHY_CAP5_NG16_MU_FEEDBACK 0x8
-#define IEEE80211_HE_PHY_CAP5_NG16_SU_FEEDBACK 0x10
-#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK 0x20
-
-#define IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT 0x1
-#define IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMER_FB 0x2
-#define IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMER_FB 0x4
-#define IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB 0x8
-#define IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB 0x20
-#define IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU 0x40
-#define IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU 0x80
-#define IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE 0x80
-#define IEEE80211_HE_PHY_CAP6_TRIG_CQI_FB 0x80
-#define IEEE80211_HE_PHY_CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO 0x80
-
-#define IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI 0x1
-#define IEEE80211_HE_PHY_CAP7_MAX_NC_1 0x2
-#define IEEE80211_HE_PHY_CAP7_MAX_NC_2 0x4
-#define IEEE80211_HE_PHY_CAP7_MAX_NC_MASK 0x6
-#define IEEE80211_HE_PHY_CAP7_POWER_BOOST_FACTOR_AR 0x8
-#define IEEE80211_HE_PHY_CAP7_POWER_BOOST_FACTOR_SUPP 0x10
-#define IEEE80211_HE_PHY_CAP7_STBC_RX_ABOVE_80MHZ 0x20
-#define IEEE80211_HE_PHY_CAP7_STBC_TX_ABOVE_80MHZ 0x40
-#define IEEE80211_HE_PHY_CAP7_PSR_BASED_SR 0x80
-
-#define IEEE80211_HE_PHY_CAP8_20MHZ_IN_160MHZ_HE_PPDU 0x1
-#define IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G 0x2
-#define IEEE80211_HE_PHY_CAP8_80MHZ_IN_160MHZ_HE_PPDU 0x4
-#define IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_242 0x8
-#define IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_484 0x10
-#define IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_996 0x18
-#define IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_2x996 0x20
-#define IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_MASK 0x28
-#define IEEE80211_HE_PHY_CAP8_HE_ER_SU_PPDU_4XLTF_AND_08_US_GI 0x40
-#define IEEE80211_HE_PHY_CAP8_HE_ER_SU_1XLTF_AND_08_US_GI 0x80
-
-#define IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_0US 0x1
-#define IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US 0x2
-#define IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_8US 0x4
-#define IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_MASK 0x8
-#define IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_RESERVED 0x10
-#define IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_POS 0x0
-#define IEEE80211_HE_PHY_CAP9_NON_TRIGGERED_CQI_FEEDBACK 0x20
-#define IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB 0x4
-#define IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB 0x8
-#define IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU 0x10
-#define IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU 0x20
-#define IEEE80211_HE_PHY_CAP9_LONGER_THAN_16_SIGB_OFDM_SYM 0x40
-
-#define IEEE80211_HE_PHY_CAP10_HE_MU_M1RU_MAX_LTF 0x1
-
-#define IEEE80211_HE_OPERATION_BSS_COLOR_DISABLED 0x1
-#define IEEE80211_HE_OPERATION_BSS_COLOR_OFFSET 0x2
-#define IEEE80211_HE_OPERATION_ER_SU_DISABLE 0x4
-
-#define IEEE80211_HE_SPR_HESIGA_SR_VAL15_ALLOWED 0x01
-#define IEEE80211_HE_SPR_NON_SRG_OBSS_PD_SR_DISALLOWED 0x02
-#define IEEE80211_HE_SPR_NON_SRG_OFFSET_PRESENT 0x04
-#define IEEE80211_HE_SPR_SRG_INFORMATION_PRESENT 0x08
-
#define IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS 0x01
#define IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_11454 0x02
#define IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_MASK 0x03
@@ -774,6 +649,7 @@ struct linuxkpi_ieee80211_regdomain {
#define IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1 0x05
#define IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE2 0x06
#define IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_7991 0x07
+#define IEEE80211_EHT_MAC_CAP0_SCS_TRAFFIC_DESC 0x08
#define IEEE80211_EHT_MAC_CAP1_MAX_AMPDU_LEN_MASK 0x01
@@ -851,28 +727,12 @@ struct linuxkpi_ieee80211_regdomain {
#define VENDOR_CMD_RAW_DATA (void *)(uintptr_t)(-ENOENT)
-struct ieee80211_he_cap_elem {
- u8 mac_cap_info[6];
- u8 phy_cap_info[11];
-} __packed;
-
-struct ieee80211_he_mcs_nss_supp {
- /* TODO FIXME */
- uint32_t rx_mcs_80;
- uint32_t tx_mcs_80;
- uint32_t rx_mcs_160;
- uint32_t tx_mcs_160;
- uint32_t rx_mcs_80p80;
- uint32_t tx_mcs_80p80;
-};
-
-#define IEEE80211_STA_HE_CAP_PPE_THRES_MAX 32
+/* net80211::net80211_he_cap */
struct ieee80211_sta_he_cap {
- /* TODO FIXME */
- int has_he;
+ bool has_he;
struct ieee80211_he_cap_elem he_cap_elem;
struct ieee80211_he_mcs_nss_supp he_mcs_nss_supp;
- uint8_t ppe_thres[IEEE80211_STA_HE_CAP_PPE_THRES_MAX];
+ uint8_t ppe_thres[IEEE80211_HE_CAP_PPE_THRES_MAX];
};
struct cfg80211_he_bss_color {
@@ -889,22 +749,27 @@ struct ieee80211_he_obss_pd {
uint8_t partial_bssid_bitmap[8];
};
-struct ieee80211_sta_he_6ghz_capa {
- /* TODO FIXME */
- int capa;
-};
-
struct ieee80211_eht_mcs_nss_supp_20mhz_only {
- uint8_t rx_tx_mcs7_max_nss;
- uint8_t rx_tx_mcs9_max_nss;
- uint8_t rx_tx_mcs11_max_nss;
- uint8_t rx_tx_mcs13_max_nss;
+ union {
+ struct {
+ uint8_t rx_tx_mcs7_max_nss;
+ uint8_t rx_tx_mcs9_max_nss;
+ uint8_t rx_tx_mcs11_max_nss;
+ uint8_t rx_tx_mcs13_max_nss;
+ };
+ uint8_t rx_tx_max_nss[4];
+ };
};
struct ieee80211_eht_mcs_nss_supp_bw {
- uint8_t rx_tx_mcs9_max_nss;
- uint8_t rx_tx_mcs11_max_nss;
- uint8_t rx_tx_mcs13_max_nss;
+ union {
+ struct {
+ uint8_t rx_tx_mcs9_max_nss;
+ uint8_t rx_tx_mcs11_max_nss;
+ uint8_t rx_tx_mcs13_max_nss;
+ };
+ uint8_t rx_tx_max_nss[3];
+ };
};
struct ieee80211_eht_cap_elem_fixed {
@@ -937,7 +802,7 @@ struct ieee80211_sband_iftype_data {
/* TODO FIXME */
enum nl80211_iftype types_mask;
struct ieee80211_sta_he_cap he_cap;
- struct ieee80211_sta_he_6ghz_capa he_6ghz_capa;
+ struct ieee80211_he_6ghz_capa he_6ghz_capa;
struct ieee80211_sta_eht_cap eht_cap;
struct {
const uint8_t *data;
@@ -981,9 +846,12 @@ struct cfg80211_wowlan_nd_info {
enum wiphy_wowlan_support_flags {
WIPHY_WOWLAN_DISCONNECT,
- WIPHY_WOWLAN_GTK_REKEY_FAILURE,
WIPHY_WOWLAN_MAGIC_PKT,
WIPHY_WOWLAN_SUPPORTS_GTK_REKEY,
+ WIPHY_WOWLAN_GTK_REKEY_FAILURE,
+ WIPHY_WOWLAN_EAP_IDENTITY_REQ,
+ WIPHY_WOWLAN_4WAY_HANDSHAKE,
+ WIPHY_WOWLAN_RFKILL_RELEASE,
WIPHY_WOWLAN_NET_DETECT,
};
@@ -997,6 +865,7 @@ struct cfg80211_wowlan_wakeup {
/* XXX TODO */
uint16_t pattern_idx;
bool disconnect;
+ bool unprot_deauth_disassoc;
bool eap_identity_req;
bool four_way_handshake;
bool gtk_rekey_failure;
@@ -1014,11 +883,22 @@ struct cfg80211_wowlan_wakeup {
struct cfg80211_wowlan {
/* XXX TODO */
- int disconnect, gtk_rekey_failure, magic_pkt;
- int eap_identity_req, four_way_handshake, rfkill_release, tcp, any;
+ bool any;
+ bool disconnect;
+ bool magic_pkt;
+ bool gtk_rekey_failure;
+ bool eap_identity_req;
+ bool four_way_handshake;
+ bool rfkill_release;
+
+ /* Magic packet patterns. */
int n_patterns;
- struct cfg80211_sched_scan_request *nd_config;
struct cfg80211_pkt_pattern *patterns;
+
+ /* netdetect? if not assoc? */
+ struct cfg80211_sched_scan_request *nd_config;
+
+ void *tcp; /* XXX ? */
};
struct cfg80211_gtk_rekey_data {
@@ -1096,7 +976,7 @@ struct wiphy_iftype_ext_capab {
const uint8_t *extended_capabilities_mask;
uint8_t extended_capabilities_len;
uint16_t eml_capabilities;
-
+ uint16_t mld_capa_and_ops;
};
struct tid_config_support {
@@ -1115,6 +995,18 @@ enum cfg80211_regulatory {
REGULATORY_COUNTRY_IE_FOLLOW_POWER = BIT(6),
};
+struct wiphy_radio_freq_range {
+ uint32_t start_freq;
+ uint32_t end_freq;
+};
+
+struct wiphy_radio {
+ int n_freq_range;
+ int n_iface_combinations;
+ const struct wiphy_radio_freq_range *freq_range;
+ const struct ieee80211_iface_combination *iface_combinations;
+};
+
enum wiphy_flags {
WIPHY_FLAG_AP_UAPSD = BIT(0),
WIPHY_FLAG_HAS_CHANNEL_SWITCH = BIT(1),
@@ -1133,10 +1025,23 @@ enum wiphy_flags {
WIPHY_FLAG_4ADDR_AP = BIT(14),
WIPHY_FLAG_4ADDR_STATION = BIT(15),
WIPHY_FLAG_SUPPORTS_MLO = BIT(16),
+ WIPHY_FLAG_DISABLE_WEXT = BIT(17),
};
-struct wiphy {
+struct wiphy_work;
+typedef void (*wiphy_work_fn)(struct wiphy *, struct wiphy_work *);
+struct wiphy_work {
+ struct list_head entry;
+ wiphy_work_fn fn;
+};
+struct wiphy_delayed_work {
+ struct wiphy_work work;
+ struct wiphy *wiphy;
+ struct timer_list timer;
+};
+struct wiphy {
+ struct mutex mtx;
struct device *dev;
struct mac_address *addresses;
int n_addresses;
@@ -1166,6 +1071,9 @@ struct wiphy {
uint8_t available_antennas_rx;
uint8_t available_antennas_tx;
+ int n_radio;
+ const struct wiphy_radio *radio;
+
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;
@@ -1179,16 +1087,22 @@ struct wiphy {
unsigned long ext_features[BITS_TO_LONGS(NUM_NL80211_EXT_FEATURES)];
struct dentry *debugfsdir;
- struct cfg80211_wowlan_support *wowlan;
+
+ const struct wiphy_wowlan_support *wowlan;
+ struct cfg80211_wowlan *wowlan_config;
/* Lower layer (driver/mac80211) specific data. */
/* Must stay last. */
uint8_t priv[0] __aligned(CACHE_LINE_SIZE);
};
+#define lockdep_assert_wiphy(wiphy) \
+ lockdep_assert_held(&(wiphy)->mtx)
+
struct wireless_dev {
/* XXX TODO, like ic? */
- int iftype;
- int address;
+ enum nl80211_iftype iftype;
+ uint32_t radio_mask;
+ uint8_t address[ETH_ALEN];
struct net_device *netdev;
struct wiphy *wiphy;
};
@@ -1249,8 +1163,18 @@ struct cfg80211_ops {
struct wiphy *linuxkpi_wiphy_new(const struct cfg80211_ops *, size_t);
void linuxkpi_wiphy_free(struct wiphy *wiphy);
+void linuxkpi_wiphy_work_queue(struct wiphy *, struct wiphy_work *);
+void linuxkpi_wiphy_work_cancel(struct wiphy *, struct wiphy_work *);
+void linuxkpi_wiphy_work_flush(struct wiphy *, struct wiphy_work *);
+void lkpi_wiphy_delayed_work_timer(struct timer_list *);
+void linuxkpi_wiphy_delayed_work_queue(struct wiphy *,
+ struct wiphy_delayed_work *, unsigned long);
+void linuxkpi_wiphy_delayed_work_cancel(struct wiphy *,
+ struct wiphy_delayed_work *);
+
int linuxkpi_regulatory_set_wiphy_regd_sync(struct wiphy *wiphy,
struct linuxkpi_ieee80211_regdomain *regd);
+uint32_t linuxkpi_cfg80211_calculate_bitrate(struct rate_info *);
uint32_t linuxkpi_ieee80211_channel_to_frequency(uint32_t, enum nl80211_band);
uint32_t linuxkpi_ieee80211_frequency_to_channel(uint32_t, uint32_t);
struct linuxkpi_ieee80211_channel *
@@ -1300,28 +1224,11 @@ wiphy_dev(struct wiphy *wiphy)
return (wiphy->dev);
}
-#define wiphy_err(_wiphy, _fmt, ...) \
- dev_err((_wiphy)->dev, _fmt, __VA_ARGS__)
+#define wiphy_dereference(_w, p) \
+ rcu_dereference_check(p, lockdep_is_held(&(_w)->mtx))
-static __inline const struct linuxkpi_ieee80211_regdomain *
-wiphy_dereference(struct wiphy *wiphy,
- const struct linuxkpi_ieee80211_regdomain *regd)
-{
- TODO();
- return (NULL);
-}
-
-static __inline void
-wiphy_lock(struct wiphy *wiphy)
-{
- TODO();
-}
-
-static __inline void
-wiphy_unlock(struct wiphy *wiphy)
-{
- TODO();
-}
+#define wiphy_lock(_w) mutex_lock(&(_w)->mtx)
+#define wiphy_unlock(_w) mutex_unlock(&(_w)->mtx)
static __inline void
wiphy_rfkill_set_hw_state_reason(struct wiphy *wiphy, bool blocked,
@@ -1372,6 +1279,18 @@ rfkill_soft_blocked(int rfkill)
return (false);
}
+static __inline void
+wiphy_rfkill_start_polling(struct wiphy *wiphy)
+{
+ TODO();
+}
+
+static __inline void
+wiphy_rfkill_stop_polling(struct wiphy *wiphy)
+{
+ TODO();
+}
+
static __inline int
reg_query_regdb_wmm(uint8_t *alpha2, uint32_t center_freq,
struct ieee80211_reg_rule *rule)
@@ -1413,40 +1332,74 @@ cfg80211_pmsr_report(struct wireless_dev *wdev,
TODO();
}
-static __inline void
+static inline void
cfg80211_chandef_create(struct cfg80211_chan_def *chandef,
- struct linuxkpi_ieee80211_channel *chan, enum nl80211_chan_flags chan_flag)
+ struct linuxkpi_ieee80211_channel *chan, enum nl80211_channel_type chan_type)
{
KASSERT(chandef != NULL, ("%s: chandef is NULL\n", __func__));
KASSERT(chan != NULL, ("%s: chan is NULL\n", __func__));
- memset(chandef, 0, sizeof(*chandef));
+ /* memset(chandef, 0, sizeof(*chandef)); */
chandef->chan = chan;
- chandef->center_freq2 = 0; /* Set here and only overwrite if needed. */
+ chandef->center_freq1 = chan->center_freq;
+ /* chandef->width, center_freq2, punctured */
- switch (chan_flag) {
+ switch (chan_type) {
case NL80211_CHAN_NO_HT:
chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
- chandef->center_freq1 = chan->center_freq;
break;
- default:
- IMPROVE("Also depends on our manual settings");
- if (chan->flags & IEEE80211_CHAN_NO_HT40)
- chandef->width = NL80211_CHAN_WIDTH_20;
- else if (chan->flags & IEEE80211_CHAN_NO_80MHZ)
- chandef->width = NL80211_CHAN_WIDTH_40;
- else if (chan->flags & IEEE80211_CHAN_NO_160MHZ)
- chandef->width = NL80211_CHAN_WIDTH_80;
- else {
- chandef->width = NL80211_CHAN_WIDTH_160;
- IMPROVE("80P80 and 320 ...");
- }
- chandef->center_freq1 = chan->center_freq;
+ case NL80211_CHAN_HT20:
+ chandef->width = NL80211_CHAN_WIDTH_20;
+ break;
+ case NL80211_CHAN_HT40MINUS:
+ chandef->width = NL80211_CHAN_WIDTH_40;
+ chandef->center_freq1 -= 10;
+ break;
+ case NL80211_CHAN_HT40PLUS:
+ chandef->width = NL80211_CHAN_WIDTH_40;
+ chandef->center_freq1 += 10;
break;
};
}
+static __inline bool
+cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
+{
+ TODO();
+ return (false);
+}
+
+static __inline bool
+cfg80211_chandef_dfs_usable(struct wiphy *wiphy, const struct cfg80211_chan_def *chandef)
+{
+ TODO();
+ return (false);
+}
+
+static __inline unsigned int
+cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy, const struct cfg80211_chan_def *chandef)
+{
+ TODO();
+ return (0);
+}
+
+static __inline bool
+cfg80211_chandef_identical(const struct cfg80211_chan_def *chandef_1,
+ const struct cfg80211_chan_def *chandef_2)
+{
+ TODO();
+ return (false);
+}
+
+static __inline bool
+cfg80211_chandef_usable(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *chandef, uint32_t flags)
+{
+ TODO();
+ return (false);
+}
+
static __inline void
cfg80211_bss_iter(struct wiphy *wiphy, struct cfg80211_chan_def *chandef,
void (*iterfunc)(struct wiphy *, struct cfg80211_bss *, void *), void *data)
@@ -1529,11 +1482,10 @@ cfg80211_find_vendor_ie(unsigned int oui, int oui_type,
return (__DECONST(uint8_t *, elem));
}
-static __inline uint32_t
+static inline uint32_t
cfg80211_calculate_bitrate(struct rate_info *rate)
{
- TODO();
- return (-1);
+ return (linuxkpi_cfg80211_calculate_bitrate(rate));
}
static __inline uint32_t
@@ -1661,6 +1613,12 @@ wiphy_ext_feature_set(struct wiphy *wiphy, enum nl80211_ext_feature ef)
set_bit(ef, wiphy->ext_features);
}
+static inline bool
+wiphy_ext_feature_isset(struct wiphy *wiphy, enum nl80211_ext_feature ef)
+{
+ return (test_bit(ef, wiphy->ext_features));
+}
+
static __inline void *
wiphy_net(struct wiphy *wiphy)
{
@@ -1906,7 +1864,7 @@ cfg80211_channel_is_psc(struct linuxkpi_ieee80211_channel *channel)
static inline int
cfg80211_get_ies_channel_number(const uint8_t *ie, size_t len,
- enum nl80211_band band, enum cfg80211_bss_frame_type ftype)
+ enum nl80211_band band)
{
const struct element *elem;
@@ -1977,23 +1935,200 @@ cfg80211_find_ext_ie(uint8_t eid, const uint8_t *p, size_t len)
return (NULL);
}
-static __inline bool
-cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
+static inline void
+_ieee80211_set_sband_iftype_data(struct ieee80211_supported_band *band,
+ struct ieee80211_sband_iftype_data *iftype_data, size_t nitems)
{
- TODO();
- return (false);
+ band->iftype_data = iftype_data;
+ band->n_iftype_data = nitems;
+}
+
+static inline const struct ieee80211_sband_iftype_data *
+ieee80211_get_sband_iftype_data(const struct ieee80211_supported_band *band,
+ enum nl80211_iftype iftype)
+{
+ const struct ieee80211_sband_iftype_data *iftype_data;
+ int i;
+
+ for (i = 0; i < band->n_iftype_data; i++) {
+ iftype_data = (const void *)&band->iftype_data[i];
+ if (iftype_data->types_mask & BIT(iftype))
+ return (iftype_data);
+ }
+
+ return (NULL);
}
-static __inline const struct ieee80211_sta_eht_cap *
+static inline const struct ieee80211_sta_he_cap *
+ieee80211_get_he_iftype_cap(const struct ieee80211_supported_band *band,
+ enum nl80211_iftype iftype)
+{
+ const struct ieee80211_sband_iftype_data *iftype_data;
+ const struct ieee80211_sta_he_cap *he_cap;
+
+ iftype_data = ieee80211_get_sband_iftype_data(band, iftype);
+ if (iftype_data == NULL)
+ return (NULL);
+
+ he_cap = NULL;
+ if (iftype_data->he_cap.has_he)
+ he_cap = &iftype_data->he_cap;
+
+ return (he_cap);
+}
+
+static inline const struct ieee80211_sta_eht_cap *
ieee80211_get_eht_iftype_cap(const struct ieee80211_supported_band *band,
enum nl80211_iftype iftype)
{
+ const struct ieee80211_sband_iftype_data *iftype_data;
+ const struct ieee80211_sta_eht_cap *eht_cap;
+
+ iftype_data = ieee80211_get_sband_iftype_data(band, iftype);
+ if (iftype_data == NULL)
+ return (NULL);
+
+ eht_cap = NULL;
+ if (iftype_data->eht_cap.has_eht)
+ eht_cap = &iftype_data->eht_cap;
+
+ return (eht_cap);
+}
+
+static inline bool
+cfg80211_ssid_eq(struct cfg80211_ssid *ssid1, struct cfg80211_ssid *ssid2)
+{
+ int error;
+
+ if (ssid1 == NULL || ssid2 == NULL) /* Can we KASSERT this? */
+ return (false);
+
+ if (ssid1->ssid_len != ssid2->ssid_len)
+ return (false);
+ error = memcmp(ssid1->ssid, ssid2->ssid, ssid2->ssid_len);
+ if (error != 0)
+ return (false);
+ return (true);
+}
+
+static inline void
+cfg80211_rx_unprot_mlme_mgmt(struct net_device *ndev, const uint8_t *hdr,
+ uint32_t len)
+{
+ TODO();
+}
+
+static inline const struct wiphy_iftype_ext_capab *
+cfg80211_get_iftype_ext_capa(struct wiphy *wiphy, enum nl80211_iftype iftype)
+{
+
TODO();
return (NULL);
}
+static inline uint16_t
+ieee80211_get_he_6ghz_capa(const struct ieee80211_supported_band *sband,
+ enum nl80211_iftype iftype)
+{
+ TODO();
+ return (0);
+}
+
+static inline int
+nl80211_chan_width_to_mhz(enum nl80211_chan_width width)
+{
+ switch (width) {
+ case NL80211_CHAN_WIDTH_5:
+ return (5);
+ break;
+ case NL80211_CHAN_WIDTH_10:
+ return (10);
+ break;
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ case NL80211_CHAN_WIDTH_20:
+ return (20);
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ return (40);
+ break;
+ case NL80211_CHAN_WIDTH_80:
+ case NL80211_CHAN_WIDTH_80P80:
+ return (80);
+ break;
+ case NL80211_CHAN_WIDTH_160:
+ return (160);
+ break;
+ case NL80211_CHAN_WIDTH_320:
+ return (320);
+ break;
+ }
+}
+
+static __inline ssize_t
+wiphy_locked_debugfs_write(struct wiphy *wiphy, struct file *file,
+ char *buf, size_t bufsize, const char __user *userbuf, size_t count,
+ ssize_t (*handler)(struct wiphy *, struct file *, char *, size_t, void *),
+ void *data)
+{
+ TODO();
+ return (-ENXIO);
+}
+
+/* -------------------------------------------------------------------------- */
+
+static inline void
+wiphy_work_init(struct wiphy_work *wwk, wiphy_work_fn fn)
+{
+ INIT_LIST_HEAD(&wwk->entry);
+ wwk->fn = fn;
+}
+
+static inline void
+wiphy_work_queue(struct wiphy *wiphy, struct wiphy_work *wwk)
+{
+ linuxkpi_wiphy_work_queue(wiphy, wwk);
+}
+
+static inline void
+wiphy_work_cancel(struct wiphy *wiphy, struct wiphy_work *wwk)
+{
+ linuxkpi_wiphy_work_cancel(wiphy, wwk);
+}
+
+static inline void
+wiphy_work_flush(struct wiphy *wiphy, struct wiphy_work *wwk)
+{
+ linuxkpi_wiphy_work_flush(wiphy, wwk);
+}
+
+static inline void
+wiphy_delayed_work_init(struct wiphy_delayed_work *wdwk, wiphy_work_fn fn)
+{
+ wiphy_work_init(&wdwk->work, fn);
+ timer_setup(&wdwk->timer, lkpi_wiphy_delayed_work_timer, 0);
+}
+
+static inline void
+wiphy_delayed_work_queue(struct wiphy *wiphy, struct wiphy_delayed_work *wdwk,
+ unsigned long delay)
+{
+ linuxkpi_wiphy_delayed_work_queue(wiphy, wdwk, delay);
+}
+
+static inline void
+wiphy_delayed_work_cancel(struct wiphy *wiphy, struct wiphy_delayed_work *wdwk)
+{
+ linuxkpi_wiphy_delayed_work_cancel(wiphy, wdwk);
+}
+
+/* -------------------------------------------------------------------------- */
+
+#define wiphy_err(_wiphy, _fmt, ...) \
+ dev_err((_wiphy)->dev, _fmt, __VA_ARGS__)
#define wiphy_info(wiphy, fmt, ...) \
- printf("%s:%d XXX TODO " fmt, __func__, __LINE__, __VA_ARGS__)
+ dev_info((wiphy)->dev, fmt, ##__VA_ARGS__)
+#define wiphy_info_once(wiphy, fmt, ...) \
+ dev_info_once((wiphy)->dev, fmt, ##__VA_ARGS__)
#ifndef LINUXKPI_NET80211
#define ieee80211_channel linuxkpi_ieee80211_channel
diff --git a/sys/compat/linuxkpi/common/include/net/mac80211.h b/sys/compat/linuxkpi/common/include/net/mac80211.h
index fc9d7829dae3..af3199c38939 100644
--- a/sys/compat/linuxkpi/common/include/net/mac80211.h
+++ b/sys/compat/linuxkpi/common/include/net/mac80211.h
@@ -1,6 +1,6 @@
/*-
- * Copyright (c) 2020-2023 The FreeBSD Foundation
- * Copyright (c) 2020-2022 Bjoern A. Zeeb
+ * Copyright (c) 2020-2025 The FreeBSD Foundation
+ * Copyright (c) 2020-2025 Bjoern A. Zeeb
*
* This software was developed by Björn Zeeb under sponsorship from
* the FreeBSD Foundation.
@@ -34,13 +34,16 @@
#include <asm/atomic64.h>
#include <linux/bitops.h>
+#include <linux/bitfield.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/workqueue.h>
#include <linux/dcache.h>
+#include <linux/ieee80211.h>
#include <net/cfg80211.h>
+#include <net/if_inet6.h>
#define ARPHRD_IEEE80211_RADIOTAP __LINE__ /* XXX TODO brcmfmac */
@@ -83,6 +86,7 @@ enum mcast_filter_flags {
FIF_OTHER_BSS = BIT(4),
FIF_PSPOLL = BIT(5),
FIF_CONTROL = BIT(6),
+ FIF_MCAST_ACTION = BIT(7),
};
enum ieee80211_bss_changed {
@@ -117,6 +121,9 @@ enum ieee80211_bss_changed {
BSS_CHANGED_TWT = BIT(28),
BSS_CHANGED_UNSOL_BCAST_PROBE_RESP = BIT(30),
BSS_CHANGED_EHT_PUNCTURING = BIT(31),
+ BSS_CHANGED_MLD_VALID_LINKS = BIT_ULL(32),
+ BSS_CHANGED_MLD_TTLM = BIT_ULL(33),
+ BSS_CHANGED_TPE = BIT_ULL(34),
};
/* 802.11 Figure 9-256 Suite selector format. [OUI(3), SUITE TYPE(1)] */
@@ -167,13 +174,24 @@ enum ieee80211_bss_changed {
#define TKIP_PN_TO_IV16(_x) ((uint16_t)(_x & 0xffff))
#define TKIP_PN_TO_IV32(_x) ((uint32_t)((_x >> 16) & 0xffffffff))
-struct ieee80211_sta;
+enum ieee80211_neg_ttlm_res {
+ NEG_TTLM_RES_ACCEPT,
+ NEG_TTLM_RES_REJECT,
+};
+
+#define IEEE80211_TTLM_NUM_TIDS 8
+struct ieee80211_neg_ttlm {
+ uint16_t downlink[IEEE80211_TTLM_NUM_TIDS];
+ uint16_t uplink[IEEE80211_TTLM_NUM_TIDS];
+};
/* 802.11-2020 9.4.2.55.3 A-MPDU Parameters field */
#define IEEE80211_HT_AMPDU_PARM_FACTOR 0x3
#define IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT 2
#define IEEE80211_HT_AMPDU_PARM_DENSITY (0x7 << IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT)
+struct ieee80211_sta;
+
struct ieee80211_ampdu_params {
struct ieee80211_sta *sta;
enum ieee80211_ampdu_mlme_action action;
@@ -191,19 +209,6 @@ struct ieee80211_bar {
uint16_t frame_control;
};
-struct ieee80211_p2p_noa_desc {
- uint32_t count; /* uint8_t ? */
- uint32_t duration;
- uint32_t interval;
- uint32_t start_time;
-};
-
-struct ieee80211_p2p_noa_attr {
- uint8_t index;
- uint8_t oppps_ctwindow;
- struct ieee80211_p2p_noa_desc desc[4];
-};
-
struct ieee80211_mutable_offsets {
/* TODO FIXME */
uint16_t tim_offset;
@@ -217,11 +222,13 @@ struct mac80211_fils_discovery {
};
struct ieee80211_chanctx_conf {
- /* TODO FIXME */
- int rx_chains_dynamic, rx_chains_static;
- bool radar_enabled;
struct cfg80211_chan_def def;
struct cfg80211_chan_def min_def;
+ struct cfg80211_chan_def ap;
+
+ uint8_t rx_chains_dynamic;
+ uint8_t rx_chains_static;
+ bool radar_enabled;
/* Must stay last. */
uint8_t drv_priv[0] __aligned(CACHE_LINE_SIZE);
@@ -240,12 +247,39 @@ struct ieee80211_ema_beacons {
} bcn[0];
};
+struct ieee80211_chanreq {
+ struct cfg80211_chan_def oper;
+};
+
#define WLAN_MEMBERSHIP_LEN (8)
#define WLAN_USER_POSITION_LEN (16)
+/*
+ * 802.11ac-2013, 8.4.2.164 VHT Transmit Power Envelope element
+ * 802.11-???? ?
+ */
+struct ieee80211_parsed_tpe_eirp {
+ int8_t power[5];
+ uint8_t count;
+ bool valid;
+};
+struct ieee80211_parsed_tpe_psd {
+ int8_t power[16];
+ uint8_t count;
+ bool valid;
+};
+struct ieee80211_parsed_tpe {
+ /* We see access to [0] so assume at least 2. */
+ struct ieee80211_parsed_tpe_eirp max_local[2];
+ struct ieee80211_parsed_tpe_eirp max_reg_client[2];
+ struct ieee80211_parsed_tpe_psd psd_local[2];
+ struct ieee80211_parsed_tpe_psd psd_reg_client[2];
+};
+
struct ieee80211_bss_conf {
/* TODO FIXME */
struct ieee80211_vif *vif;
+ struct cfg80211_bss *bss;
const uint8_t *bssid;
uint8_t addr[ETH_ALEN];
uint8_t link_id;
@@ -253,7 +287,7 @@ struct ieee80211_bss_conf {
uint8_t transmitter_bssid[ETH_ALEN];
struct ieee80211_ftm_responder_params *ftmr_params;
struct ieee80211_p2p_noa_attr p2p_noa_attr;
- struct cfg80211_chan_def chandef;
+ struct ieee80211_chanreq chanreq;
__be32 arp_addr_list[1]; /* XXX TODO */
struct ieee80211_rate *beacon_rate;
struct {
@@ -288,6 +322,7 @@ struct ieee80211_bss_conf {
uint8_t dtim_period;
uint8_t sync_dtim_count;
+ uint8_t bss_param_ch_cnt_link_id;
bool qos;
bool twt_broadcast;
bool use_cts_prot;
@@ -297,17 +332,19 @@ struct ieee80211_bss_conf {
bool eht_support;
bool csa_active;
bool mu_mimo_owner;
+ bool color_change_active;
uint32_t sync_device_ts;
uint64_t sync_tsf;
uint16_t beacon_int;
int16_t txpower;
uint32_t basic_rates;
int mcast_rate[NUM_NL80211_BANDS];
- enum ieee80211_reg_ap_power power_type;
+ enum ieee80211_ap_reg_power power_type;
struct cfg80211_bitrate_mask beacon_tx_rate;
struct mac80211_fils_discovery fils_discovery;
struct ieee80211_chanctx_conf *chanctx_conf;
struct ieee80211_vif *mbssid_tx_vif;
+ struct ieee80211_parsed_tpe tpe;
int ack_enabled, bssid_index, bssid_indicator, cqm_rssi_hyst, cqm_rssi_thold, ema_ap, frame_time_rts_th, ftm_responder;
int htc_trig_based_pkt_ext;
@@ -316,27 +353,15 @@ struct ieee80211_bss_conf {
int twt_requester, uora_exists, uora_ocw_range;
int assoc_capability, enable_beacon, hidden_ssid, ibss_joined, twt_protected;
int twt_responder, unsol_bcast_probe_resp_interval;
- int color_change_active;
};
struct ieee80211_channel_switch {
/* TODO FIXME */
int block_tx, count, delay, device_timestamp, timestamp;
+ uint8_t link_id;
struct cfg80211_chan_def chandef;
};
-struct ieee80211_cipher_scheme {
- uint32_t cipher;
- uint8_t iftype; /* We do not know the size of this. */
- uint8_t hdr_len;
- uint8_t pn_len;
- uint8_t pn_off;
- uint8_t key_idx_off;
- uint8_t key_idx_mask;
- uint8_t key_idx_shift;
- uint8_t mic_len;
-};
-
enum ieee80211_event_type {
BA_FRAME_TIMEOUT,
BAR_RX_EVENT,
@@ -388,11 +413,6 @@ struct ieee80211_ftm_responder_params {
int civicloc_len;
};
-struct ieee80211_he_mu_edca_param_ac_rec {
- /* TODO FIXME */
- int aifsn, ecw_min_max, mu_edca_timer;
-};
-
struct ieee80211_conf {
int dynamic_ps_timeout;
int power_level;
@@ -444,6 +464,11 @@ enum ieee80211_hw_flags {
IEEE80211_HW_SUPPORTS_TX_ENCAP_OFFLOAD,
IEEE80211_HW_SUPPORTS_RC_TABLE,
IEEE80211_HW_DETECTS_COLOR_COLLISION,
+ IEEE80211_HW_DISALLOW_PUNCTURING,
+ IEEE80211_HW_DISALLOW_PUNCTURING_5GHZ,
+ IEEE80211_HW_TX_STATUS_NO_AMPDU_LEN,
+ IEEE80211_HW_HANDLES_QUIET_CSA,
+ IEEE80211_HW_NO_VIRTUAL_MONITOR,
/* Keep last. */
NUM_IEEE80211_HW_FLAGS
@@ -456,8 +481,6 @@ struct ieee80211_hw {
/* TODO FIXME */
int extra_tx_headroom, weight_multiplier;
int max_rate_tries, max_rates, max_report_rates;
- struct ieee80211_cipher_scheme *cipher_schemes;
- int n_cipher_schemes;
const char *rate_control_algorithm;
struct {
uint16_t units_pos; /* radiotap "spec" is .. inconsistent. */
@@ -500,9 +523,18 @@ enum ieee802111_key_flag {
IEEE80211_KEY_FLAG_GENERATE_IV_MGMT = BIT(6),
IEEE80211_KEY_FLAG_GENERATE_MMIE = BIT(7),
IEEE80211_KEY_FLAG_RESERVE_TAILROOM = BIT(8),
+ IEEE80211_KEY_FLAG_SPP_AMSDU = BIT(9),
};
+#define IEEE80211_KEY_FLAG_BITS \
+ "\20\1GENERATE_IV\2GENERATE_MMIC\3PAIRWISE\4PUT_IV_SPACE" \
+ "\5PUT_MIC_SPACE\6SW_MGMT_TX\7GENERATE_IV_MGMT\10GENERATE_MMIE" \
+ "\11RESERVE_TAILROOM\12SPP_AMSDU"
+
struct ieee80211_key_conf {
+#if defined(__FreeBSD__)
+ const struct ieee80211_key *_k; /* backpointer to net80211 */
+#endif
atomic64_t tx_pn;
uint32_t cipher;
uint8_t icv_len; /* __unused nowadays? */
@@ -526,10 +558,13 @@ struct ieee80211_key_seq {
uint8_t pn[IEEE80211_CCMP_PN_LEN];
} ccmp;
struct {
- uint8_t pn[IEEE80211_CCMP_PN_LEN];
+ uint8_t pn[IEEE80211_GCMP_PN_LEN];
+ } gcmp;
+ struct {
+ uint8_t pn[IEEE80211_CMAC_PN_LEN];
} aes_cmac;
struct {
- uint8_t pn[IEEE80211_CCMP_PN_LEN];
+ uint8_t pn[IEEE80211_GMAC_PN_LEN];
} aes_gmac;
struct {
uint32_t iv32;
@@ -548,8 +583,10 @@ enum ieee80211_rx_status_flags {
RX_FLAG_DUP_VALIDATED = BIT(5),
RX_FLAG_FAILED_FCS_CRC = BIT(6),
RX_FLAG_ICV_STRIPPED = BIT(7),
- RX_FLAG_MACTIME_PLCP_START = BIT(8),
- RX_FLAG_MACTIME_START = BIT(9),
+ RX_FLAG_MACTIME = BIT(8) | BIT(9),
+ RX_FLAG_MACTIME_PLCP_START = 1 << 8,
+ RX_FLAG_MACTIME_START = 2 << 8,
+ RX_FLAG_MACTIME_END = 3 << 8,
RX_FLAG_MIC_STRIPPED = BIT(10),
RX_FLAG_MMIC_ERROR = BIT(11),
RX_FLAG_MMIC_STRIPPED = BIT(12),
@@ -564,13 +601,27 @@ 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),
- RX_FLAG_MACTIME_END = BIT(24),
+ /* = BIT(24), */
RX_FLAG_ONLY_MONITOR = BIT(25),
RX_FLAG_SKIP_MONITOR = BIT(26),
RX_FLAG_8023 = BIT(27),
RX_FLAG_RADIOTAP_TLV_AT_END = BIT(28),
+ /* = BIT(29), */
+ RX_FLAG_MACTIME_IS_RTAP_TS64 = BIT(30),
+ RX_FLAG_FAILED_PLCP_CRC = BIT(31),
};
+#define IEEE80211_RX_STATUS_FLAGS_BITS \
+ "\20\1ALLOW_SAME_PN\2AMPDU_DETAILS\3AMPDU_EOF_BIT\4AMPDU_EOF_BIT_KNOWN" \
+ "\5DECRYPTED\6DUP_VALIDATED\7FAILED_FCS_CRC\10ICV_STRIPPED" \
+ "\11MACTIME_PLCP_START\12MACTIME_START\13MIC_STRIPPED" \
+ "\14MMIC_ERROR\15MMIC_STRIPPED\16NO_PSDU\17PN_VALIDATED" \
+ "\20RADIOTAP_HE\21RADIOTAP_HE_MU\22RADIOTAP_LSIG\23RADIOTAP_VENDOR_DATA" \
+ "\24NO_SIGNAL_VAL\25IV_STRIPPED\26AMPDU_IS_LAST\27AMPDU_LAST_KNOWN" \
+ "\30AMSDU_MORE\31MACTIME_END\32ONLY_MONITOR\33SKIP_MONITOR" \
+ "\348023\35RADIOTAP_TLV_AT_END\36MACTIME\37MACTIME_IS_RTAP_TS64" \
+ "\40FAILED_PLCP_CRC"
+
enum mac80211_rx_encoding {
RX_ENC_LEGACY = 0,
RX_ENC_HT,
@@ -653,9 +704,10 @@ struct ieee80211_sta_rates {
/* XXX TODO */
/* XXX some _rcu thing */
struct {
- int idx;
- int flags;
- } rate[1]; /* XXX what is the real number? */
+ uint8_t idx;
+ uint8_t count;
+ uint16_t flags;
+ } rate[4]; /* XXX what is the real number? */
};
struct ieee80211_sta_txpwr {
@@ -672,13 +724,14 @@ struct ieee80211_sta_agg {
};
struct ieee80211_link_sta {
+ struct ieee80211_sta *sta;
uint8_t addr[ETH_ALEN];
uint8_t link_id;
uint32_t supp_rates[NUM_NL80211_BANDS];
struct ieee80211_sta_ht_cap ht_cap;
struct ieee80211_sta_vht_cap vht_cap;
struct ieee80211_sta_he_cap he_cap;
- struct ieee80211_sta_he_6ghz_capa he_6ghz_capa;
+ struct ieee80211_he_6ghz_capa he_6ghz_capa;
struct ieee80211_sta_eht_cap eht_cap;
uint8_t rx_nss;
enum ieee80211_sta_rx_bw bandwidth;
@@ -696,6 +749,7 @@ struct ieee80211_sta {
uint8_t addr[ETH_ALEN];
uint16_t aid;
bool wme;
+ bool mlo;
uint8_t max_sp;
uint8_t uapsd_queues;
uint16_t valid_links;
@@ -742,7 +796,12 @@ enum ieee80211_vif_driver_flags {
IEEE80211_VIF_BEACON_FILTER = BIT(0),
IEEE80211_VIF_SUPPORTS_CQM_RSSI = BIT(1),
IEEE80211_VIF_SUPPORTS_UAPSD = BIT(2),
- IEEE80211_VIF_DISABLE_SMPS_OVERRIDE = BIT(3),
+#if defined(LINUXKPI_VERSION) && (LINUXKPI_VERSION < 60600) /* v6.6 */
+ IEEE80211_VIF_DISABLE_SMPS_OVERRIDE = BIT(3), /* Renamed to IEEE80211_VIF_EML_ACTIVE. */
+#endif
+ IEEE80211_VIF_EML_ACTIVE = BIT(4),
+ IEEE80211_VIF_IGNORE_OFDMA_WIDER_BW = BIT(5),
+ IEEE80211_VIF_REMOVE_AP_AFTER_DISASSOC = BIT(6),
};
#define IEEE80211_BSS_ARP_ADDR_LIST_LEN 4
@@ -765,15 +824,13 @@ struct ieee80211_vif_cfg {
struct ieee80211_vif {
/* TODO FIXME */
enum nl80211_iftype type;
- int csa_active, mu_mimo_owner;
int cab_queue;
- int color_change_active, offload_flags;
+ int offload_flags;
enum ieee80211_vif_driver_flags driver_flags;
bool p2p;
bool probe_req_reg;
uint8_t addr[ETH_ALEN];
struct ieee80211_vif_cfg cfg;
- struct ieee80211_chanctx_conf *chanctx_conf;
struct ieee80211_txq *txq;
struct ieee80211_bss_conf bss_conf;
struct ieee80211_bss_conf *link_conf[IEEE80211_MLD_MAX_NUM_LINKS]; /* rcu? */
@@ -799,20 +856,23 @@ struct ieee80211_vif_chanctx_switch {
struct ieee80211_prep_tx_info {
u16 duration;
bool success;
+ bool was_assoc;
+ int link_id;
};
/* XXX-BZ too big, over-reduce size to u8, and array sizes to minuimum to fit in skb->cb. */
/* Also warning: some sizes change by pointer size! This is 64bit only. */
struct ieee80211_tx_info {
- enum ieee80211_tx_info_flags flags;
+ enum ieee80211_tx_info_flags flags; /* 32 bits */
/* TODO FIXME */
- u8 band;
- u8 hw_queue;
- bool tx_time_est;
+ enum nl80211_band band; /* 3 bits */
+ uint16_t hw_queue:4, /* 4 bits */
+ tx_time_est:10; /* 10 bits */
union {
struct {
struct ieee80211_tx_rate rates[4];
bool use_rts;
+ uint8_t antennas:2;
struct ieee80211_vif *vif;
struct ieee80211_key_conf *hw_key;
enum ieee80211_tx_control_flags flags;
@@ -911,11 +971,12 @@ enum ieee80211_offload_flags {
struct ieee80211_ops {
/* TODO FIXME */
int (*start)(struct ieee80211_hw *);
- void (*stop)(struct ieee80211_hw *);
+ void (*stop)(struct ieee80211_hw *, bool);
int (*config)(struct ieee80211_hw *, u32);
void (*reconfig_complete)(struct ieee80211_hw *, enum ieee80211_reconfig_type);
+ void (*prep_add_interface)(struct ieee80211_hw *, enum nl80211_iftype);
int (*add_interface)(struct ieee80211_hw *, struct ieee80211_vif *);
void (*remove_interface)(struct ieee80211_hw *, struct ieee80211_vif *);
int (*change_interface)(struct ieee80211_hw *, struct ieee80211_vif *, enum nl80211_iftype, bool);
@@ -934,7 +995,7 @@ struct ieee80211_ops {
void (*mgd_prepare_tx)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_prep_tx_info *);
void (*mgd_complete_tx)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_prep_tx_info *);
- void (*mgd_protect_tdls_discover)(struct ieee80211_hw *, struct ieee80211_vif *);
+ void (*mgd_protect_tdls_discover)(struct ieee80211_hw *, struct ieee80211_vif *, unsigned int);
void (*flush)(struct ieee80211_hw *, struct ieee80211_vif *, u32, bool);
void (*flush_sta)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *);
@@ -954,6 +1015,7 @@ struct ieee80211_ops {
int (*sta_state)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *, enum ieee80211_sta_state, enum ieee80211_sta_state);
void (*sta_notify)(struct ieee80211_hw *, struct ieee80211_vif *, enum sta_notify_cmd, struct ieee80211_sta *);
void (*sta_rc_update)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *, u32);
+ void (*link_sta_rc_update)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_link_sta *, u32);
void (*sta_rate_tbl_update)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *);
void (*sta_set_4addr)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *, bool);
void (*sta_set_decap_offload)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *, bool);
@@ -965,10 +1027,10 @@ struct ieee80211_ops {
bool (*can_aggregate_in_amsdu)(struct ieee80211_hw *, struct sk_buff *, struct sk_buff *);
int (*pre_channel_switch)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_channel_switch *);
- int (*post_channel_switch)(struct ieee80211_hw *, struct ieee80211_vif *);
+ int (*post_channel_switch)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_bss_conf *);
void (*channel_switch)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_channel_switch *);
void (*channel_switch_beacon)(struct ieee80211_hw *, struct ieee80211_vif *, struct cfg80211_chan_def *);
- void (*abort_channel_switch)(struct ieee80211_hw *, struct ieee80211_vif *);
+ void (*abort_channel_switch)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_bss_conf *);
void (*channel_switch_rx_beacon)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_channel_switch *);
int (*tdls_channel_switch)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *, u8, struct cfg80211_chan_def *, struct sk_buff *, u32);
void (*tdls_cancel_channel_switch)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *);
@@ -1007,9 +1069,7 @@ struct ieee80211_ops {
int (*set_tim)(struct ieee80211_hw *, struct ieee80211_sta *, bool);
int (*set_key)(struct ieee80211_hw *, enum set_key_cmd, struct ieee80211_vif *, struct ieee80211_sta *, struct ieee80211_key_conf *);
- void (*set_default_unicast_key)(struct ieee80211_hw *, struct ieee80211_vif *, int);
void (*update_tkip_key)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_key_conf *, struct ieee80211_sta *, u32, u16 *);
- void (*set_rekey_data)(struct ieee80211_hw *, struct ieee80211_vif *, struct cfg80211_gtk_rekey_data *);
int (*start_pmsr)(struct ieee80211_hw *, struct ieee80211_vif *, struct cfg80211_pmsr_request *);
void (*abort_pmsr)(struct ieee80211_hw *, struct ieee80211_vif *, struct cfg80211_pmsr_request *);
@@ -1030,7 +1090,7 @@ struct ieee80211_ops {
void (*update_vif_offload)(struct ieee80211_hw *, struct ieee80211_vif *);
- int (*get_txpower)(struct ieee80211_hw *, struct ieee80211_vif *, int *);
+ int (*get_txpower)(struct ieee80211_hw *, struct ieee80211_vif *, unsigned int, int *);
int (*get_stats)(struct ieee80211_hw *, struct ieee80211_low_level_stats *);
int (*set_radar_background)(struct ieee80211_hw *, struct cfg80211_chan_def *);
@@ -1044,13 +1104,29 @@ struct ieee80211_ops {
int (*change_vif_links)(struct ieee80211_hw *, struct ieee80211_vif *, u16, u16, struct ieee80211_bss_conf *[IEEE80211_MLD_MAX_NUM_LINKS]);
int (*change_sta_links)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *, u16, u16);
+ bool (*can_activate_links)(struct ieee80211_hw *, struct ieee80211_vif *, u16);
+ enum ieee80211_neg_ttlm_res (*can_neg_ttlm)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_neg_ttlm *);
+
+ void (*rfkill_poll)(struct ieee80211_hw *);
/* #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 *);
+ void (*link_sta_add_debugfs)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_link_sta *, struct dentry *);
+ void (*link_add_debugfs)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_bss_conf *, struct dentry *);
/* #endif */
+/* #ifdef CONFIG_PM_SLEEP */ /* Do not change depending on compile-time option. */
+ int (*suspend)(struct ieee80211_hw *, struct cfg80211_wowlan *);
+ int (*resume)(struct ieee80211_hw *);
+ void (*set_wakeup)(struct ieee80211_hw *, bool);
+ void (*set_rekey_data)(struct ieee80211_hw *, struct ieee80211_vif *, struct cfg80211_gtk_rekey_data *);
+ void (*set_default_unicast_key)(struct ieee80211_hw *, struct ieee80211_vif *, int);
+/* #if IS_ENABLED(CONFIG_IPV6) */
+ void (*ipv6_addr_change)(struct ieee80211_hw *, struct ieee80211_vif *, struct inet6_dev *);
+/* #endif */
+/* #endif CONFIG_PM_SLEEP */
};
-
/* -------------------------------------------------------------------------- */
/* linux_80211.c */
@@ -1073,7 +1149,7 @@ void linuxkpi_ieee80211_iterate_keys(struct ieee80211_hw *,
struct ieee80211_vif *,
void(*iterfunc)(struct ieee80211_hw *, struct ieee80211_vif *,
struct ieee80211_sta *, struct ieee80211_key_conf *, void *),
- void *);
+ void *, bool);
void linuxkpi_ieee80211_iterate_chan_contexts(struct ieee80211_hw *,
void(*iterfunc)(struct ieee80211_hw *,
struct ieee80211_chanctx_conf *, void *),
@@ -1253,7 +1329,7 @@ ieee80211_hw_restart_disconnect(struct ieee80211_vif *vif)
(_link = rcu_dereference((_vif)->link_conf[_linkid])) )
#define for_each_sta_active_link(_vif, _sta, _linksta, _linkid) \
- for (_linkid = 0; _linkid < nitems((_vif)->link_conf); _linkid++) \
+ for (_linkid = 0; _linkid < nitems((_sta)->link); _linkid++) \
if ( ((_vif)->active_links == 0 /* no MLO */ || \
((_vif)->active_links & BIT(_linkid)) != 0) && \
(_linksta = link_sta_dereference_protected((_sta), (_linkid))) )
@@ -1261,353 +1337,12 @@ ieee80211_hw_restart_disconnect(struct ieee80211_vif *vif)
/* -------------------------------------------------------------------------- */
static __inline bool
-ieee80211_is_action(__le16 fc)
-{
- __le16 v;
-
- fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK);
- v = htole16(IEEE80211_FC0_SUBTYPE_ACTION | IEEE80211_FC0_TYPE_MGT);
-
- return (fc == v);
-}
-
-static __inline bool
-ieee80211_is_probe_resp(__le16 fc)
-{
- __le16 v;
-
- fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK);
- v = htole16(IEEE80211_FC0_SUBTYPE_PROBE_RESP | IEEE80211_FC0_TYPE_MGT);
-
- return (fc == v);
-}
-
-static __inline bool
-ieee80211_is_auth(__le16 fc)
-{
- __le16 v;
-
- fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK);
- v = htole16(IEEE80211_FC0_SUBTYPE_AUTH | IEEE80211_FC0_TYPE_MGT);
-
- return (fc == v);
-}
-
-static __inline bool
-ieee80211_is_assoc_req(__le16 fc)
-{
- __le16 v;
-
- fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK);
- v = htole16(IEEE80211_FC0_SUBTYPE_ASSOC_REQ | IEEE80211_FC0_TYPE_MGT);
-
- return (fc == v);
-}
-
-static __inline bool
-ieee80211_is_assoc_resp(__le16 fc)
-{
- __le16 v;
-
- fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK);
- v = htole16(IEEE80211_FC0_SUBTYPE_ASSOC_RESP | IEEE80211_FC0_TYPE_MGT);
-
- return (fc == v);
-}
-
-static __inline bool
-ieee80211_is_reassoc_req(__le16 fc)
-{
- __le16 v;
-
- fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK);
- v = htole16(IEEE80211_FC0_SUBTYPE_REASSOC_REQ | IEEE80211_FC0_TYPE_MGT);
-
- return (fc == v);
-}
-
-static __inline bool
-ieee80211_is_reassoc_resp(__le16 fc)
-{
- __le16 v;
-
- fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK);
- v = htole16(IEEE80211_FC0_SUBTYPE_REASSOC_RESP | IEEE80211_FC0_TYPE_MGT);
-
- return (fc == v);
-}
-
-static __inline bool
-ieee80211_is_disassoc(__le16 fc)
-{
- __le16 v;
-
- fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK);
- v = htole16(IEEE80211_FC0_SUBTYPE_DISASSOC | IEEE80211_FC0_TYPE_MGT);
-
- return (fc == v);
-}
-
-static __inline bool
-ieee80211_is_data_present(__le16 fc)
-{
- __le16 v;
-
- /* If it is a data frame and NODATA is not present. */
- fc &= htole16(IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_NODATA);
- v = htole16(IEEE80211_FC0_TYPE_DATA);
-
- return (fc == v);
-}
-
-static __inline bool
-ieee80211_is_deauth(__le16 fc)
-{
- __le16 v;
-
- fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK);
- v = htole16(IEEE80211_FC0_SUBTYPE_DEAUTH | IEEE80211_FC0_TYPE_MGT);
-
- return (fc == v);
-}
-
-static __inline bool
-ieee80211_is_beacon(__le16 fc)
-{
- __le16 v;
-
- /*
- * For as much as I get it this comes in LE and unlike FreeBSD
- * where we get the entire frame header and u8[], here we get the
- * 9.2.4.1 Frame Control field only. Mask and compare.
- */
- fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK);
- v = htole16(IEEE80211_FC0_SUBTYPE_BEACON | IEEE80211_FC0_TYPE_MGT);
-
- return (fc == v);
-}
-
-
-static __inline bool
-ieee80211_is_probe_req(__le16 fc)
-{
- __le16 v;
-
- fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK);
- v = htole16(IEEE80211_FC0_SUBTYPE_PROBE_REQ | IEEE80211_FC0_TYPE_MGT);
-
- return (fc == v);
-}
-
-static __inline bool
-ieee80211_has_protected(__le16 fc)
-{
-
- return (fc & htole16(IEEE80211_FC1_PROTECTED << 8));
-}
-
-static __inline bool
-ieee80211_is_back_req(__le16 fc)
-{
- __le16 v;
-
- fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK);
- v = htole16(IEEE80211_FC0_SUBTYPE_BAR | IEEE80211_FC0_TYPE_CTL);
-
- return (fc == v);
-}
-
-static __inline bool
-ieee80211_is_bufferable_mmpdu(struct sk_buff *skb)
-{
- struct ieee80211_mgmt *mgmt;
- __le16 fc;
-
- mgmt = (struct ieee80211_mgmt *)skb->data;
- fc = mgmt->frame_control;
-
- /* 11.2.2 Bufferable MMPDUs, 80211-2020. */
- /* XXX we do not care about IBSS yet. */
-
- if (!ieee80211_is_mgmt(fc))
- return (false);
- if (ieee80211_is_action(fc)) /* XXX FTM? */
- return (true); /* XXX false? */
- if (ieee80211_is_disassoc(fc))
- return (true);
- if (ieee80211_is_deauth(fc))
- return (true);
-
- TODO();
-
- return (false);
-}
-
-static __inline bool
-ieee80211_is_nullfunc(__le16 fc)
-{
- __le16 v;
-
- fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK);
- v = htole16(IEEE80211_FC0_SUBTYPE_NODATA | IEEE80211_FC0_TYPE_DATA);
-
- return (fc == v);
-}
-
-static __inline bool
-ieee80211_is_qos_nullfunc(__le16 fc)
-{
- __le16 v;
-
- fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK);
- v = htole16(IEEE80211_FC0_SUBTYPE_QOS_NULL | IEEE80211_FC0_TYPE_DATA);
-
- return (fc == v);
-}
-
-static __inline bool
-ieee80211_is_any_nullfunc(__le16 fc)
-{
-
- return (ieee80211_is_nullfunc(fc) || ieee80211_is_qos_nullfunc(fc));
-}
-
-static inline bool
-ieee80211_is_pspoll(__le16 fc)
-{
- __le16 v;
-
- fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK);
- v = htole16(IEEE80211_FC0_SUBTYPE_PS_POLL | IEEE80211_FC0_TYPE_CTL);
-
- return (fc == v);
-}
-
-static __inline bool
ieee80211_vif_is_mesh(struct ieee80211_vif *vif)
{
TODO();
return (false);
}
-static __inline bool
-ieee80211_is_frag(struct ieee80211_hdr *hdr)
-{
- TODO();
- return (false);
-}
-
-static __inline bool
-ieee80211_is_first_frag(__le16 fc)
-{
- TODO();
- return (false);
-}
-
-static __inline bool
-ieee80211_is_robust_mgmt_frame(struct sk_buff *skb)
-{
- TODO();
- return (false);
-}
-
-static __inline bool
-ieee80211_is_ftm(struct sk_buff *skb)
-{
- TODO();
- return (false);
-}
-
-static __inline bool
-ieee80211_is_timing_measurement(struct sk_buff *skb)
-{
- TODO();
- return (false);
-}
-
-static __inline bool
-ieee80211_has_pm(__le16 fc)
-{
- TODO();
- return (false);
-}
-
-static __inline bool
-ieee80211_has_a4(__le16 fc)
-{
- __le16 v;
-
- fc &= htole16((IEEE80211_FC1_DIR_TODS | IEEE80211_FC1_DIR_FROMDS) << 8);
- v = htole16((IEEE80211_FC1_DIR_TODS | IEEE80211_FC1_DIR_FROMDS) << 8);
-
- return (fc == v);
-}
-
-static __inline bool
-ieee80211_has_order(__le16 fc)
-{
-
- return (fc & htole16(IEEE80211_FC1_ORDER << 8));
-}
-
-static __inline bool
-ieee80211_has_retry(__le16 fc)
-{
-
- return (fc & htole16(IEEE80211_FC1_RETRY << 8));
-}
-
-
-static __inline bool
-ieee80211_has_fromds(__le16 fc)
-{
-
- return (fc & htole16(IEEE80211_FC1_DIR_FROMDS << 8));
-}
-
-static __inline bool
-ieee80211_has_tods(__le16 fc)
-{
-
- return (fc & htole16(IEEE80211_FC1_DIR_TODS << 8));
-}
-
-static __inline uint8_t *
-ieee80211_get_SA(struct ieee80211_hdr *hdr)
-{
-
- if (ieee80211_has_a4(hdr->frame_control))
- return (hdr->addr4);
- if (ieee80211_has_fromds(hdr->frame_control))
- return (hdr->addr3);
- return (hdr->addr2);
-}
-
-static __inline uint8_t *
-ieee80211_get_DA(struct ieee80211_hdr *hdr)
-{
-
- if (ieee80211_has_tods(hdr->frame_control))
- return (hdr->addr3);
- return (hdr->addr1);
-}
-
-static __inline bool
-ieee80211_has_morefrags(__le16 fc)
-{
-
- fc &= htole16(IEEE80211_FC1_MORE_FRAG << 8);
- return (fc != 0);
-}
-
-static __inline u8 *
-ieee80211_get_qos_ctl(struct ieee80211_hdr *hdr)
-{
- if (ieee80211_has_a4(hdr->frame_control))
- return (u8 *)hdr + 30;
- else
- return (u8 *)hdr + 24;
-}
/* -------------------------------------------------------------------------- */
/* Receive functions (air/driver to mac80211/net80211). */
@@ -1714,6 +1449,13 @@ ieee80211_handle_wake_tx_queue(struct ieee80211_hw *hw,
linuxkpi_ieee80211_handle_wake_tx_queue(hw, txq);
}
+static inline void
+ieee80211_purge_tx_queue(struct ieee80211_hw *hw,
+ struct sk_buff_head *skbs)
+{
+ TODO();
+}
+
/* -------------------------------------------------------------------------- */
static __inline uint8_t
@@ -1780,25 +1522,22 @@ ieee80211_iterate_interfaces(struct ieee80211_hw *hw,
linuxkpi_ieee80211_iterate_interfaces(hw, flags, iterfunc, arg);
}
-static __inline void
+static inline void
ieee80211_iter_keys(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
void(*iterfunc)(struct ieee80211_hw *, struct ieee80211_vif *,
struct ieee80211_sta *, struct ieee80211_key_conf *, void *),
void *arg)
{
-
- linuxkpi_ieee80211_iterate_keys(hw, vif, iterfunc, arg);
+ linuxkpi_ieee80211_iterate_keys(hw, vif, iterfunc, arg, false);
}
-static __inline void
+static inline void
ieee80211_iter_keys_rcu(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
void(*iterfunc)(struct ieee80211_hw *, struct ieee80211_vif *,
struct ieee80211_sta *, struct ieee80211_key_conf *, void *),
void *arg)
{
-
- IMPROVE(); /* "rcu" */
- linuxkpi_ieee80211_iterate_keys(hw, vif, iterfunc, arg);
+ linuxkpi_ieee80211_iterate_keys(hw, vif, iterfunc, arg, true);
}
static __inline void
@@ -1841,7 +1580,7 @@ ieee80211_beacon_loss(struct ieee80211_vif *vif)
}
static __inline void
-ieee80211_chswitch_done(struct ieee80211_vif *vif, bool t)
+ieee80211_chswitch_done(struct ieee80211_vif *vif, bool t, uint32_t link_id)
{
TODO();
}
@@ -1867,12 +1606,12 @@ ieee80211_csa_update_counter(struct ieee80211_vif *vif)
}
static __inline void
-ieee80211_csa_finish(struct ieee80211_vif *vif)
+ieee80211_csa_finish(struct ieee80211_vif *vif, uint32_t link_id)
{
TODO();
}
-static __inline enum nl80211_iftype
+static inline enum nl80211_iftype
ieee80211_vif_type_p2p(struct ieee80211_vif *vif)
{
@@ -1970,26 +1709,6 @@ ieee80211_find_sta_by_ifaddr(struct ieee80211_hw *hw, const uint8_t *addr,
return (linuxkpi_ieee80211_find_sta_by_ifaddr(hw, addr, ourvifaddr));
}
-
-static __inline void
-ieee80211_get_tkip_p2k(struct ieee80211_key_conf *keyconf,
- struct sk_buff *skb_frag, u8 *key)
-{
- TODO();
-}
-
-static __inline void
-ieee80211_get_tkip_rx_p1k(struct ieee80211_key_conf *keyconf,
- const u8 *addr, uint32_t iv32, u16 *p1k)
-{
-
- KASSERT(keyconf != NULL && addr != NULL && p1k != NULL,
- ("%s: keyconf %p addr %p p1k %p\n", __func__, keyconf, addr, p1k));
-
- TODO();
- memset(p1k, 0xfa, 5 * sizeof(*p1k)); /* Just initializing. */
-}
-
static __inline size_t
ieee80211_ie_split(const u8 *ies, size_t ies_len,
const u8 *ie_ids, size_t ie_ids_len, size_t start)
@@ -2077,91 +1796,211 @@ ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif,
TODO();
}
-static __inline void
-ieee80211_mark_rx_ba_filtered_frames(struct ieee80211_sta *sta, uint8_t tid,
- uint32_t ssn, uint64_t bitmap, uint16_t received_mpdu)
-{
- TODO();
-}
+/* -------------------------------------------------------------------------- */
-static __inline bool
+static inline bool
ieee80211_sn_less(uint16_t sn1, uint16_t sn2)
{
- TODO();
- return (false);
+ return (IEEE80211_SEQ_BA_BEFORE(sn1, sn2));
}
-static __inline uint16_t
+static inline uint16_t
ieee80211_sn_inc(uint16_t sn)
{
- TODO();
- return (sn + 1);
+ return (IEEE80211_SEQ_INC(sn));
}
-static __inline uint16_t
+static inline uint16_t
ieee80211_sn_add(uint16_t sn, uint16_t a)
{
- TODO();
- return (sn + a);
+ return (IEEE80211_SEQ_ADD(sn, a));
}
-static __inline void
-ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, uint32_t x, uint8_t *addr)
+static inline uint16_t
+ieee80211_sn_sub(uint16_t sa, uint16_t sb)
{
- TODO();
+ return (IEEE80211_SEQ_SUB(sa, sb));
}
static __inline void
-ieee80211_rate_set_vht(struct ieee80211_tx_rate *r, uint32_t f1, uint32_t f2)
+ieee80211_mark_rx_ba_filtered_frames(struct ieee80211_sta *sta, uint8_t tid,
+ uint32_t ssn, uint64_t bitmap, uint16_t received_mpdu)
{
TODO();
}
-static __inline uint8_t
-ieee80211_rate_get_vht_nss(struct ieee80211_tx_rate *r)
+static __inline void
+ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, uint32_t x, uint8_t *addr)
{
TODO();
- return (0);
}
-static __inline uint8_t
-ieee80211_rate_get_vht_mcs(struct ieee80211_tx_rate *r)
+static __inline void
+ieee80211_rx_ba_timer_expired(struct ieee80211_vif *vif, uint8_t *addr,
+ uint8_t tid)
{
TODO();
- return (0);
}
static __inline void
-ieee80211_reserve_tid(struct ieee80211_sta *sta, uint8_t tid)
+ieee80211_start_rx_ba_session_offl(struct ieee80211_vif *vif, uint8_t *addr,
+ uint8_t tid)
{
TODO();
}
static __inline void
-ieee80211_unreserve_tid(struct ieee80211_sta *sta, uint8_t tid)
+ieee80211_stop_rx_ba_session_offl(struct ieee80211_vif *vif, uint8_t *addr,
+ uint8_t tid)
{
TODO();
}
+/* -------------------------------------------------------------------------- */
+
+static inline void
+ieee80211_rate_set_vht(struct ieee80211_tx_rate *r, uint8_t mcs, uint8_t nss)
+{
+
+ /* XXX-BZ make it KASSERTS? */
+ if (((mcs & 0xF0) != 0) || (((nss - 1) & 0xf8) != 0)) {
+ printf("%s:%d: mcs %#04x nss %#04x invalid\n",
+ __func__, __LINE__, mcs, nss);
+ return;
+ }
+
+ r->idx = mcs;
+ r->idx |= ((nss - 1) << 4);
+}
+
+static inline uint8_t
+ieee80211_rate_get_vht_nss(const struct ieee80211_tx_rate *r)
+{
+ return (((r->idx >> 4) & 0x07) + 1);
+}
+
+static inline uint8_t
+ieee80211_rate_get_vht_mcs(const struct ieee80211_tx_rate *r)
+{
+ return (r->idx & 0x0f);
+}
+
+static inline int
+ieee80211_get_vht_max_nss(struct ieee80211_vht_cap *vht_cap,
+ enum ieee80211_vht_chanwidth chanwidth, /* defined in net80211. */
+ int mcs /* always 0 */, bool ext_nss_bw_cap /* always true */, int max_nss)
+{
+ enum ieee80211_vht_mcs_support mcs_s;
+ uint32_t supp_cw, ext_nss_bw;
+
+ switch (mcs) {
+ case 0 ... 7:
+ mcs_s = IEEE80211_VHT_MCS_SUPPORT_0_7;
+ break;
+ case 8:
+ mcs_s = IEEE80211_VHT_MCS_SUPPORT_0_8;
+ break;
+ case 9:
+ mcs_s = IEEE80211_VHT_MCS_SUPPORT_0_9;
+ break;
+ default:
+ printf("%s: unsupported mcs value %d\n", __func__, mcs);
+ return (0);
+ }
+
+ if (max_nss == 0) {
+ uint16_t map;
+
+ map = le16toh(vht_cap->supp_mcs.rx_mcs_map);
+ for (int i = 7; i >= 0; i--) {
+ uint8_t val;
+
+ val = (map >> (2 * i)) & 0x03;
+ if (val == IEEE80211_VHT_MCS_NOT_SUPPORTED)
+ continue;
+ if (val >= mcs_s) {
+ max_nss = i + 1;
+ break;
+ }
+ }
+ }
+
+ if (max_nss == 0)
+ return (0);
+
+ if ((le16toh(vht_cap->supp_mcs.tx_mcs_map) &
+ IEEE80211_VHT_EXT_NSS_BW_CAPABLE) == 0)
+ return (max_nss);
+
+ supp_cw = le32_get_bits(vht_cap->vht_cap_info,
+ IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK);
+ ext_nss_bw = le32_get_bits(vht_cap->vht_cap_info,
+ IEEE80211_VHT_CAP_EXT_NSS_BW_MASK);
+
+ /* If requested as ext nss not supported assume ext_nss_bw 0. */
+ if (!ext_nss_bw_cap)
+ ext_nss_bw = 0;
+
+ /*
+ * Cover 802.11-2016, Table 9-250.
+ */
+
+ /* Unsupported settings. */
+ if (supp_cw == 3)
+ return (0);
+ if (supp_cw == 2 && (ext_nss_bw == 1 || ext_nss_bw == 2))
+ return (0);
+
+ /* Settings with factor != 1 or unsupported. */
+ switch (chanwidth) {
+ case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
+ if (supp_cw == 0 && (ext_nss_bw == 0 || ext_nss_bw == 1))
+ return (0);
+ if (supp_cw == 1 && ext_nss_bw == 0)
+ return (0);
+ if ((supp_cw == 0 || supp_cw == 1) && ext_nss_bw == 2)
+ return (max_nss / 2);
+ if ((supp_cw == 0 || supp_cw == 1) && ext_nss_bw == 3)
+ return (3 * max_nss / 4);
+ break;
+ case IEEE80211_VHT_CHANWIDTH_160MHZ:
+ if (supp_cw == 0 && ext_nss_bw == 0)
+ return (0);
+ if (supp_cw == 0 && (ext_nss_bw == 1 || ext_nss_bw == 2))
+ return (max_nss / 2);
+ if (supp_cw == 0 && ext_nss_bw == 3)
+ return (3 * max_nss / 4);
+ if (supp_cw == 1 && ext_nss_bw == 3)
+ return (2 * max_nss);
+ break;
+ case IEEE80211_VHT_CHANWIDTH_80MHZ:
+ case IEEE80211_VHT_CHANWIDTH_USE_HT:
+ if ((supp_cw == 1 || supp_cw == 2) && ext_nss_bw == 3)
+ return (2 * max_nss);
+ break;
+ }
+
+ /* Everything else has a factor of 1. */
+ return (max_nss);
+}
+
+
static __inline void
-ieee80211_rx_ba_timer_expired(struct ieee80211_vif *vif, uint8_t *addr,
- uint8_t tid)
+ieee80211_reserve_tid(struct ieee80211_sta *sta, uint8_t tid)
{
TODO();
}
static __inline void
-ieee80211_send_eosp_nullfunc(struct ieee80211_sta *sta, uint8_t tid)
+ieee80211_unreserve_tid(struct ieee80211_sta *sta, uint8_t tid)
{
TODO();
}
-static __inline uint16_t
-ieee80211_sn_sub(uint16_t sa, uint16_t sb)
+static __inline void
+ieee80211_send_eosp_nullfunc(struct ieee80211_sta *sta, uint8_t tid)
{
-
- return ((sa - sb) &
- (IEEE80211_SEQ_SEQ_MASK >> IEEE80211_SEQ_SEQ_SHIFT));
+ TODO();
}
static __inline void
@@ -2183,10 +2022,12 @@ ieee80211_sta_pspoll(struct ieee80211_sta *sta)
TODO();
}
-static __inline void
+static inline void
ieee80211_sta_recalc_aggregates(struct ieee80211_sta *sta)
{
- TODO();
+ if (sta->valid_links) {
+ TODO();
+ }
}
static __inline void
@@ -2195,13 +2036,6 @@ ieee80211_sta_uapsd_trigger(struct ieee80211_sta *sta, int ntids)
TODO();
}
-static __inline void
-ieee80211_tkip_add_iv(u8 *crypto_hdr, struct ieee80211_key_conf *keyconf,
- uint64_t pn)
-{
- TODO();
-}
-
static inline struct sk_buff *
ieee80211_tx_dequeue(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
{
@@ -2235,33 +2069,6 @@ ieee80211_sta_set_buffered(struct ieee80211_sta *sta, uint8_t tid, bool t)
}
static __inline void
-ieee80211_get_key_rx_seq(struct ieee80211_key_conf *keyconf, uint8_t tid,
- struct ieee80211_key_seq *seq)
-{
-
- KASSERT(keyconf != NULL && seq != NULL, ("%s: keyconf %p seq %p\n",
- __func__, keyconf, seq));
-
- TODO();
- switch (keyconf->cipher) {
- case WLAN_CIPHER_SUITE_CCMP:
- case WLAN_CIPHER_SUITE_CCMP_256:
- memset(seq->ccmp.pn, 0xfa, sizeof(seq->ccmp.pn)); /* XXX TODO */
- break;
- case WLAN_CIPHER_SUITE_AES_CMAC:
- memset(seq->aes_cmac.pn, 0xfa, sizeof(seq->aes_cmac.pn)); /* XXX TODO */
- break;
- case WLAN_CIPHER_SUITE_TKIP:
- seq->tkip.iv32 = 0xfa; /* XXX TODO */
- seq->tkip.iv16 = 0xfa; /* XXX TODO */
- break;
- default:
- pr_debug("%s: unsupported cipher suite %d\n", __func__, keyconf->cipher);
- break;
- }
-}
-
-static __inline void
ieee80211_sched_scan_results(struct ieee80211_hw *hw)
{
TODO();
@@ -2377,25 +2184,39 @@ ieee80211_queue_work(struct ieee80211_hw *hw, struct work_struct *w)
linuxkpi_ieee80211_queue_work(hw, w);
}
-static __inline void
-ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
+static __inline bool
+ieee80211_tx_prepare_skb(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct sk_buff *skb, enum nl80211_band band, struct ieee80211_sta **sta)
{
+ TODO();
+ return (false);
+}
+static __inline void
+ieee80211_tx_status_skb(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
linuxkpi_ieee80211_tx_status(hw, skb);
}
+static inline void
+ieee80211_tx_status_noskb(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
+ struct ieee80211_tx_info *info)
+{
+ TODO();
+}
+
static __inline void
ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb)
{
IMPROVE();
- ieee80211_tx_status(hw, skb);
+ linuxkpi_ieee80211_tx_status(hw, skb);
}
static __inline void
ieee80211_tx_status_ni(struct ieee80211_hw *hw, struct sk_buff *skb)
{
IMPROVE();
- ieee80211_tx_status(hw, skb);
+ linuxkpi_ieee80211_tx_status(hw, skb);
}
static __inline void
@@ -2437,15 +2258,6 @@ ieee80211_txq_get_depth(struct ieee80211_txq *txq, unsigned long *frame_cnt,
linuxkpi_ieee80211_txq_get_depth(txq, frame_cnt, byte_cnt);
}
-static __inline int
-rate_lowest_index(struct ieee80211_supported_band *band,
- struct ieee80211_sta *sta)
-{
- IMPROVE();
- return (0);
-}
-
-
static __inline void
SET_IEEE80211_PERM_ADDR (struct ieee80211_hw *hw, uint8_t *addr)
{
@@ -2460,20 +2272,6 @@ ieee80211_report_low_ack(struct ieee80211_sta *sta, int x)
}
static __inline void
-ieee80211_start_rx_ba_session_offl(struct ieee80211_vif *vif, uint8_t *addr,
- uint8_t tid)
-{
- TODO();
-}
-
-static __inline void
-ieee80211_stop_rx_ba_session_offl(struct ieee80211_vif *vif, uint8_t *addr,
- uint8_t tid)
-{
- TODO();
-}
-
-static __inline void
ieee80211_tx_rate_update(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
struct ieee80211_tx_info *info)
{
@@ -2488,7 +2286,8 @@ ieee80211_txq_may_transmit(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
}
static __inline void
-ieee80211_radar_detected(struct ieee80211_hw *hw)
+ieee80211_radar_detected(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *chanctx_conf)
{
TODO();
}
@@ -2507,22 +2306,14 @@ ieee80211_beacon_set_cntdwn(struct ieee80211_vif *vif, u8 counter)
}
static __inline int
-ieee80211_beacon_update_cntdwn(struct ieee80211_vif *vif)
-{
- TODO();
- return (-1);
-}
-
-static __inline int
-ieee80211_get_vht_max_nss(struct ieee80211_vht_cap *vht_cap, uint32_t chanwidth,
- int x, bool t, int nss)
+ieee80211_beacon_update_cntdwn(struct ieee80211_vif *vif, uint32_t link_id)
{
TODO();
return (-1);
}
static __inline bool
-ieee80211_beacon_cntdwn_is_complete(struct ieee80211_vif *vif)
+ieee80211_beacon_cntdwn_is_complete(struct ieee80211_vif *vif, uint32_t link_id)
{
TODO();
return (true);
@@ -2535,27 +2326,7 @@ ieee80211_disconnect(struct ieee80211_vif *vif, bool _x)
}
static __inline void
-ieee80211_channel_switch_disconnect(struct ieee80211_vif *vif, bool _x)
-{
- TODO();
-}
-
-static __inline const struct ieee80211_sta_he_cap *
-ieee80211_get_he_iftype_cap(const struct ieee80211_supported_band *band,
- enum nl80211_iftype type)
-{
- TODO();
- return (NULL);
-}
-
-static __inline void
-ieee80211_key_mic_failure(struct ieee80211_key_conf *key)
-{
- TODO();
-}
-
-static __inline void
-ieee80211_key_replay(struct ieee80211_key_conf *key)
+ieee80211_channel_switch_disconnect(struct ieee80211_vif *vif)
{
TODO();
}
@@ -2576,7 +2347,7 @@ ieee80211_get_tx_rates(struct ieee80211_vif *vif, struct ieee80211_sta *sta,
}
static __inline void
-ieee80211_color_change_finish(struct ieee80211_vif *vif)
+ieee80211_color_change_finish(struct ieee80211_vif *vif, uint8_t link_id)
{
TODO();
}
@@ -2618,16 +2389,29 @@ ieee80211_data_to_8023(struct sk_buff *skb, const uint8_t *addr,
return (-1);
}
+/* -------------------------------------------------------------------------- */
+
static __inline void
-ieee80211_get_tkip_p1k_iv(struct ieee80211_key_conf *key,
- uint32_t iv32, uint16_t *p1k)
+ieee80211_key_mic_failure(struct ieee80211_key_conf *key)
+{
+ TODO();
+}
+
+static __inline void
+ieee80211_key_replay(struct ieee80211_key_conf *key)
+{
+ TODO();
+}
+
+static __inline void
+ieee80211_remove_key(struct ieee80211_key_conf *key)
{
TODO();
}
static __inline struct ieee80211_key_conf *
ieee80211_gtk_rekey_add(struct ieee80211_vif *vif,
- struct ieee80211_key_conf *key)
+ struct ieee80211_key_conf *key, int link_id)
{
TODO();
return (NULL);
@@ -2641,18 +2425,103 @@ ieee80211_gtk_rekey_notify(struct ieee80211_vif *vif, const uint8_t *bssid,
}
static __inline void
-ieee80211_remove_key(struct ieee80211_key_conf *key)
+ieee80211_tkip_add_iv(u8 *crypto_hdr, struct ieee80211_key_conf *keyconf,
+ uint64_t pn)
+{
+ TODO();
+}
+
+static __inline void
+ieee80211_get_tkip_rx_p1k(struct ieee80211_key_conf *keyconf,
+ const u8 *addr, uint32_t iv32, u16 *p1k)
+{
+
+ KASSERT(keyconf != NULL && addr != NULL && p1k != NULL,
+ ("%s: keyconf %p addr %p p1k %p\n", __func__, keyconf, addr, p1k));
+
+ TODO();
+ memset(p1k, 0xfa, 5 * sizeof(*p1k)); /* Just initializing. */
+}
+
+static __inline void
+ieee80211_get_tkip_p1k_iv(struct ieee80211_key_conf *key,
+ uint32_t iv32, uint16_t *p1k)
{
TODO();
}
static __inline void
+ieee80211_get_tkip_p2k(struct ieee80211_key_conf *keyconf,
+ struct sk_buff *skb_frag, u8 *key)
+{
+ TODO();
+}
+
+static inline void
+ieee80211_get_key_rx_seq(struct ieee80211_key_conf *keyconf, int8_t tid,
+ struct ieee80211_key_seq *seq)
+{
+ const struct ieee80211_key *k;
+ const uint8_t *p;
+
+ KASSERT(keyconf != NULL && seq != NULL, ("%s: keyconf %p seq %p\n",
+ __func__, keyconf, seq));
+ k = keyconf->_k;
+ KASSERT(k != NULL, ("%s: keyconf %p ieee80211_key is NULL\n", __func__, keyconf));
+
+ switch (keyconf->cipher) {
+ case WLAN_CIPHER_SUITE_TKIP:
+ if (tid < 0 || tid >= IEEE80211_NUM_TIDS)
+ return;
+ /* See net80211::tkip_decrypt() */
+ seq->tkip.iv32 = TKIP_PN_TO_IV32(k->wk_keyrsc[tid]);
+ seq->tkip.iv16 = TKIP_PN_TO_IV16(k->wk_keyrsc[tid]);
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_CCMP_256:
+ if (tid < -1 || tid >= IEEE80211_NUM_TIDS)
+ return;
+ if (tid == -1)
+ p = (const uint8_t *)&k->wk_keyrsc[IEEE80211_NUM_TIDS]; /* IEEE80211_NONQOS_TID */
+ else
+ p = (const uint8_t *)&k->wk_keyrsc[tid];
+ memcpy(seq->ccmp.pn, p, sizeof(seq->ccmp.pn));
+ break;
+ case WLAN_CIPHER_SUITE_GCMP:
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ if (tid < -1 || tid >= IEEE80211_NUM_TIDS)
+ return;
+ if (tid == -1)
+ p = (const uint8_t *)&k->wk_keyrsc[IEEE80211_NUM_TIDS]; /* IEEE80211_NONQOS_TID */
+ else
+ p = (const uint8_t *)&k->wk_keyrsc[tid];
+ memcpy(seq->gcmp.pn, p, sizeof(seq->gcmp.pn));
+ break;
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+ TODO();
+ memset(seq->aes_cmac.pn, 0xfa, sizeof(seq->aes_cmac.pn)); /* XXX TODO */
+ break;
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+ TODO();
+ memset(seq->aes_gmac.pn, 0xfa, sizeof(seq->aes_gmac.pn)); /* XXX TODO */
+ break;
+ default:
+ pr_debug("%s: unsupported cipher suite %d\n", __func__, keyconf->cipher);
+ break;
+ }
+}
+
+static __inline void
ieee80211_set_key_rx_seq(struct ieee80211_key_conf *key, int tid,
struct ieee80211_key_seq *seq)
{
TODO();
}
+/* -------------------------------------------------------------------------- */
+
static __inline void
ieee80211_report_wowlan_wakeup(struct ieee80211_vif *vif,
struct cfg80211_wowlan_wakeup *wakeup, gfp_t gfp)
@@ -2696,23 +2565,122 @@ ieee80211_vif_is_mld(const struct ieee80211_vif *vif)
return (vif->valid_links != 0);
}
-static __inline const struct ieee80211_sta_he_cap *
+static inline const struct ieee80211_sta_he_cap *
ieee80211_get_he_iftype_cap_vif(const struct ieee80211_supported_band *band,
struct ieee80211_vif *vif)
{
- TODO();
- return (NULL);
+ enum nl80211_iftype iftype;
+
+ iftype = ieee80211_vif_type_p2p(vif);
+ return (ieee80211_get_he_iftype_cap(band, iftype));
}
-static __inline const struct ieee80211_sta_eht_cap *
+static inline const struct ieee80211_sta_eht_cap *
ieee80211_get_eht_iftype_cap_vif(const struct ieee80211_supported_band *band,
struct ieee80211_vif *vif)
{
+ enum nl80211_iftype iftype;
+
+ iftype = ieee80211_vif_type_p2p(vif);
+ return (ieee80211_get_eht_iftype_cap(band, iftype));
+}
+
+static inline uint32_t
+ieee80211_vif_usable_links(const struct ieee80211_vif *vif)
+{
+ IMPROVE("MLO usable links likely are not just valid");
+ return (vif->valid_links);
+}
+
+static inline bool
+ieee80211_vif_link_active(const struct ieee80211_vif *vif, uint8_t link_id)
+{
+ if (ieee80211_vif_is_mld(vif))
+ return (vif->active_links & BIT(link_id));
+ return (link_id == 0);
+}
+
+static inline void
+ieee80211_set_active_links_async(struct ieee80211_vif *vif,
+ uint32_t new_active_links)
+{
TODO();
- return (NULL);
+}
+
+static inline int
+ieee80211_set_active_links(struct ieee80211_vif *vif,
+ uint32_t active_links)
+{
+ TODO();
+ return (-ENXIO);
+}
+
+static inline void
+ieee80211_cqm_beacon_loss_notify(struct ieee80211_vif *vif, gfp_t gfp __unused)
+{
+ IMPROVE("we notify user space by a vap state change eventually");
+ linuxkpi_ieee80211_beacon_loss(vif);
}
#define ieee80211_send_bar(_v, _r, _t, _s) \
linuxkpi_ieee80211_send_bar(_v, _r, _t, _s)
+/* -------------------------------------------------------------------------- */
+
+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)
+{
+ 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);
+}
+
+/* -------------------------------------------------------------------------- */
+
#endif /* _LINUXKPI_NET_MAC80211_H */
diff --git a/sys/compat/linuxkpi/common/include/net/page_pool.h b/sys/compat/linuxkpi/common/include/net/page_pool.h
index 82dbeff82167..2dc8f74b31f3 100644
--- a/sys/compat/linuxkpi/common/include/net/page_pool.h
+++ b/sys/compat/linuxkpi/common/include/net/page_pool.h
@@ -29,6 +29,7 @@
#include <linux/kernel.h> /* pr_debug */
#include <linux/types.h>
#include <linux/dma-mapping.h>
+#include <linux/netdevice.h>
struct device;
@@ -41,6 +42,7 @@ struct page_pool_params {
uint32_t offset;
int nid; /* NUMA */
enum dma_data_direction dma_dir;
+ struct napi_struct *napi;
};
struct page_pool {
diff --git a/sys/compat/linuxkpi/common/include/stdarg.h b/sys/compat/linuxkpi/common/include/stdarg.h
index ab2fdf7534e5..698ac45e9198 100644
--- a/sys/compat/linuxkpi/common/include/stdarg.h
+++ b/sys/compat/linuxkpi/common/include/stdarg.h
@@ -28,6 +28,6 @@
#ifndef _LINUXKPI_STDARG_H_
#define _LINUXKPI_STDARG_H_
-#include <machine/stdarg.h>
+#include <sys/stdarg.h>
#endif /* _LINUXKPI_STDARG_H_ */
diff --git a/sys/compat/linuxkpi/common/include/video/cmdline.h b/sys/compat/linuxkpi/common/include/video/cmdline.h
new file mode 100644
index 000000000000..eaa9a998fda2
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/video/cmdline.h
@@ -0,0 +1,44 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Serenity Cyber Security, LLC.
+ *
+ * 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 _VIDEO_CMDLINE_H_
+#define _VIDEO_CMDLINE_H_
+
+#include <linux/types.h>
+
+#define CONFIG_VIDEO_CMDLINE
+
+#if defined(CONFIG_VIDEO_CMDLINE)
+const char *video_get_options(const char *name);
+#else
+static inline const char *
+video_get_options(const char *name)
+{
+ return (NULL);
+}
+#endif
+#endif /* _VIDEO_CMDLINE_H_ */
diff --git a/sys/compat/linuxkpi/common/include/video/vga.h b/sys/compat/linuxkpi/common/include/video/vga.h
index e327246cc457..a5012d9e2f3f 100644
--- a/sys/compat/linuxkpi/common/include/video/vga.h
+++ b/sys/compat/linuxkpi/common/include/video/vga.h
@@ -3,6 +3,9 @@
#ifndef _LINUXKPI_VIDEO_VGA_H
#define _LINUXKPI_VIDEO_VGA_H
+#include <linux/types.h>
+#include <linux/io.h>
+
#define VGA_MIS_W 0x3c2
#define VGA_SEQ_I 0x3c4
#define VGA_SEQ_D 0x3c5
diff --git a/sys/compat/linuxkpi/common/src/linux_80211.c b/sys/compat/linuxkpi/common/src/linux_80211.c
index d598e9af0050..1d00e8da8f9a 100644
--- a/sys/compat/linuxkpi/common/src/linux_80211.c
+++ b/sys/compat/linuxkpi/common/src/linux_80211.c
@@ -1,6 +1,6 @@
/*-
- * Copyright (c) 2020-2023 The FreeBSD Foundation
- * Copyright (c) 2020-2022 Bjoern A. Zeeb
+ * Copyright (c) 2020-2025 The FreeBSD Foundation
+ * Copyright (c) 2020-2025 Bjoern A. Zeeb
*
* This software was developed by Björn Zeeb under sponsorship from
* the FreeBSD Foundation.
@@ -39,6 +39,12 @@
* We call the internal versions lxxx (e.g., hw -> lhw, sta -> lsta).
*/
+/*
+ * TODO:
+ * - lots :)
+ * - HW_CRYPTO: we need a "keystore" and an ordered list for suspend/resume.
+ */
+
#include <sys/param.h>
#include <sys/types.h>
#include <sys/kernel.h>
@@ -46,6 +52,7 @@
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/mutex.h>
+#include <sys/sbuf.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <sys/queue.h>
@@ -67,15 +74,20 @@
#include <net/mac80211.h>
#include <linux/workqueue.h>
+#include <linux/rculist.h>
#include "linux_80211.h"
#define LKPI_80211_WME
-/* #define LKPI_80211_HW_CRYPTO */
-/* #define LKPI_80211_VHT */
-/* #define LKPI_80211_HT */
+#define LKPI_80211_HW_CRYPTO
+#define LKPI_80211_HT
+#define LKPI_80211_VHT
+
#if defined(LKPI_80211_VHT) && !defined(LKPI_80211_HT)
#define LKPI_80211_HT
#endif
+#if defined(LKPI_80211_HT) && !defined(LKPI_80211_HW_CRYPTO)
+#define LKPI_80211_HW_CRYPTO
+#endif
static MALLOC_DEFINE(M_LKPI80211, "lkpi80211", "LinuxKPI 80211 compat");
@@ -87,14 +99,24 @@ static MALLOC_DEFINE(M_LKPI80211, "lkpi80211", "LinuxKPI 80211 compat");
/* -------------------------------------------------------------------------- */
-/* Keep public for as long as header files are using it too. */
-int linuxkpi_debug_80211;
-
-#ifdef LINUXKPI_DEBUG_80211
SYSCTL_DECL(_compat_linuxkpi);
SYSCTL_NODE(_compat_linuxkpi, OID_AUTO, 80211, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
"LinuxKPI 802.11 compatibility layer");
+#if defined(LKPI_80211_HW_CRYPTO)
+static bool lkpi_hwcrypto = false;
+SYSCTL_BOOL(_compat_linuxkpi_80211, OID_AUTO, hw_crypto, CTLFLAG_RDTUN,
+ &lkpi_hwcrypto, 0, "Enable LinuxKPI 802.11 hardware crypto offload");
+
+static bool lkpi_hwcrypto_tkip = false;
+SYSCTL_BOOL(_compat_linuxkpi_80211, OID_AUTO, tkip, CTLFLAG_RDTUN,
+ &lkpi_hwcrypto_tkip, 0, "Enable LinuxKPI 802.11 TKIP crypto offload");
+#endif
+
+/* Keep public for as long as header files are using it too. */
+int linuxkpi_debug_80211;
+
+#ifdef LINUXKPI_DEBUG_80211
SYSCTL_INT(_compat_linuxkpi_80211, OID_AUTO, debug, CTLFLAG_RWTUN,
&linuxkpi_debug_80211, 0, "LinuxKPI 802.11 debug level");
@@ -145,28 +167,301 @@ const struct cfg80211_ops linuxkpi_mac80211cfgops = {
static struct lkpi_sta *lkpi_find_lsta_by_ni(struct lkpi_vif *,
struct ieee80211_node *);
#endif
+static void lkpi_80211_txq_tx_one(struct lkpi_sta *, struct mbuf *);
static void lkpi_80211_txq_task(void *, int);
static void lkpi_80211_lhw_rxq_task(void *, int);
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 void lkpi_ieee80211_wake_queues_locked(struct ieee80211_hw *);
+
+static const char *
+lkpi_rate_info_bw_to_str(enum rate_info_bw bw)
+{
+
+ switch (bw) {
+
+ case RATE_INFO_BW_20:
+ return ("20");
+ break;
+ case RATE_INFO_BW_5:
+ return ("5");
+ break;
+ case RATE_INFO_BW_10:
+ return ("10");
+ break;
+ case RATE_INFO_BW_40:
+ return ("40");
+ break;
+ case RATE_INFO_BW_80:
+ return ("80");
+ break;
+ case RATE_INFO_BW_160:
+ return ("160");
+ break;
+ case RATE_INFO_BW_HE_RU:
+ IMPROVE("nl80211_he_ru_alloc");
+ return ("HE_RU");
+ break;
+ case RATE_INFO_BW_320:
+ return ("320");
+ break;
+ case RATE_INFO_BW_EHT_RU:
+ IMPROVE("nl80211_eht_ru_alloc");
+ return ("EHT_RU");
+ break;
+ default:
+ return ("?");
+ break;
+ }
+}
+
+static void
+lkpi_nl80211_sta_info_to_str(struct sbuf *s, const char *prefix,
+ const uint64_t flags)
+{
+ int bit, i;
+
+ sbuf_printf(s, "%s %#010jx", prefix, flags);
+
+ i = 0;
+ for (bit = 0; bit < BITS_PER_TYPE(flags); bit++) {
+
+ if ((flags & BIT_ULL(bit)) == 0)
+ continue;
+
+#define EXPAND_CASE(_flag) \
+ case NL80211_STA_INFO_ ## _flag: \
+ sbuf_printf(s, "%c%s", (i == 0) ? '<' : ',', #_flag); \
+ i++; \
+ break;
+
+ switch (bit) {
+ EXPAND_CASE(BEACON_RX)
+ EXPAND_CASE(BEACON_SIGNAL_AVG)
+ EXPAND_CASE(BSS_PARAM)
+ EXPAND_CASE(CHAIN_SIGNAL)
+ EXPAND_CASE(CHAIN_SIGNAL_AVG)
+ EXPAND_CASE(CONNECTED_TIME)
+ EXPAND_CASE(INACTIVE_TIME)
+ EXPAND_CASE(SIGNAL)
+ EXPAND_CASE(SIGNAL_AVG)
+ EXPAND_CASE(STA_FLAGS)
+ EXPAND_CASE(RX_BITRATE)
+ EXPAND_CASE(RX_PACKETS)
+ EXPAND_CASE(RX_BYTES)
+ EXPAND_CASE(RX_DROP_MISC)
+ EXPAND_CASE(TX_BITRATE)
+ EXPAND_CASE(TX_PACKETS)
+ EXPAND_CASE(TX_BYTES)
+ EXPAND_CASE(TX_BYTES64)
+ EXPAND_CASE(RX_BYTES64)
+ EXPAND_CASE(TX_FAILED)
+ EXPAND_CASE(TX_RETRIES)
+ EXPAND_CASE(RX_DURATION)
+ EXPAND_CASE(TX_DURATION)
+ EXPAND_CASE(ACK_SIGNAL)
+ EXPAND_CASE(ACK_SIGNAL_AVG)
+ default:
+ sbuf_printf(s, "%c?%d", (i == 0) ? '<' : ',', bit);
+ break;
+ }
+ }
+#undef EXPAND_CASE
+ if (i > 0)
+ sbuf_printf(s, ">");
+ sbuf_printf(s, "\n");
+}
+
+static int
+lkpi_80211_dump_stas(SYSCTL_HANDLER_ARGS)
+{
+ struct lkpi_hw *lhw;
+ struct ieee80211_hw *hw;
+ struct ieee80211vap *vap;
+ struct lkpi_vif *lvif;
+ struct ieee80211_vif *vif;
+ struct lkpi_sta *lsta;
+ struct ieee80211_sta *sta;
+ struct station_info sinfo;
+ struct sbuf s;
+ int error;
+
+ if (req->newptr)
+ return (EPERM);
+
+ lvif = (struct lkpi_vif *)arg1;
+ vif = LVIF_TO_VIF(lvif);
+ vap = LVIF_TO_VAP(lvif);
+ lhw = vap->iv_ic->ic_softc;
+ hw = LHW_TO_HW(lhw);
+
+ sbuf_new_for_sysctl(&s, NULL, 1024, req);
+
+ wiphy_lock(hw->wiphy);
+ list_for_each_entry(lsta, &lvif->lsta_list, lsta_list) {
+ sta = LSTA_TO_STA(lsta);
+
+ sbuf_putc(&s, '\n');
+ sbuf_printf(&s, "lsta %p sta %p added_to_drv %d\n", lsta, sta, lsta->added_to_drv);
+
+ memset(&sinfo, 0, sizeof(sinfo));
+ error = lkpi_80211_mo_sta_statistics(hw, vif, sta, &sinfo);
+ if (error == EEXIST) /* Not added to driver. */
+ continue;
+ if (error == ENOTSUPP) {
+ sbuf_printf(&s, " sta_statistics not supported\n");
+ continue;
+ }
+ if (error != 0) {
+ sbuf_printf(&s, " sta_statistics failed: %d\n", error);
+ continue;
+ }
+
+ /* If no RX_BITRATE is reported, try to fill it in from the lsta sinfo. */
+ if ((sinfo.filled & BIT_ULL(NL80211_STA_INFO_RX_BITRATE)) == 0 &&
+ (lsta->sinfo.filled & BIT_ULL(NL80211_STA_INFO_RX_BITRATE)) != 0) {
+ memcpy(&sinfo.rxrate, &lsta->sinfo.rxrate, sizeof(sinfo.rxrate));
+ sinfo.filled |= BIT_ULL(NL80211_STA_INFO_RX_BITRATE);
+ }
+
+ lkpi_nl80211_sta_info_to_str(&s, " nl80211_sta_info (valid fields)", sinfo.filled);
+ sbuf_printf(&s, " connected_time %u inactive_time %u\n",
+ sinfo.connected_time, sinfo.inactive_time);
+ sbuf_printf(&s, " rx_bytes %ju rx_packets %u rx_dropped_misc %u\n",
+ (uintmax_t)sinfo.rx_bytes, sinfo.rx_packets, sinfo.rx_dropped_misc);
+ sbuf_printf(&s, " rx_duration %ju rx_beacon %u rx_beacon_signal_avg %d\n",
+ (uintmax_t)sinfo.rx_duration, sinfo.rx_beacon, (int8_t)sinfo.rx_beacon_signal_avg);
+
+ sbuf_printf(&s, " tx_bytes %ju tx_packets %u tx_failed %u\n",
+ (uintmax_t)sinfo.tx_bytes, sinfo.tx_packets, sinfo.tx_failed);
+ sbuf_printf(&s, " tx_duration %ju tx_retries %u\n",
+ (uintmax_t)sinfo.tx_duration, sinfo.tx_retries);
+
+ sbuf_printf(&s, " signal %d signal_avg %d ack_signal %d avg_ack_signal %d\n",
+ sinfo.signal, sinfo.signal_avg, sinfo.ack_signal, sinfo.avg_ack_signal);
+
+ sbuf_printf(&s, " generation %d assoc_req_ies_len %zu chains %d\n",
+ sinfo.generation, sinfo.assoc_req_ies_len, sinfo.chains);
+
+ for (int i = 0; i < sinfo.chains && i < IEEE80211_MAX_CHAINS; i++) {
+ sbuf_printf(&s, " chain[%d] signal %d signal_avg %d\n",
+ i, (int8_t)sinfo.chain_signal[i], (int8_t)sinfo.chain_signal_avg[i]);
+ }
+
+ /* assoc_req_ies, bss_param, sta_flags */
+
+ sbuf_printf(&s, " rxrate: flags %b bw %u(%s) legacy %u kbit/s mcs %u nss %u\n",
+ sinfo.rxrate.flags, CFG80211_RATE_INFO_FLAGS_BITS,
+ sinfo.rxrate.bw, lkpi_rate_info_bw_to_str(sinfo.rxrate.bw),
+ sinfo.rxrate.legacy * 100,
+ sinfo.rxrate.mcs, sinfo.rxrate.nss);
+ sbuf_printf(&s, " he_dcm %u he_gi %u he_ru_alloc %u eht_gi %u\n",
+ sinfo.rxrate.he_dcm, sinfo.rxrate.he_gi, sinfo.rxrate.he_ru_alloc,
+ sinfo.rxrate.eht_gi);
+ sbuf_printf(&s, " txrate: flags %b bw %u(%s) legacy %u kbit/s mcs %u nss %u\n",
+ sinfo.txrate.flags, CFG80211_RATE_INFO_FLAGS_BITS,
+ sinfo.txrate.bw, lkpi_rate_info_bw_to_str(sinfo.txrate.bw),
+ sinfo.txrate.legacy * 100,
+ sinfo.txrate.mcs, sinfo.txrate.nss);
+ sbuf_printf(&s, " he_dcm %u he_gi %u he_ru_alloc %u eht_gi %u\n",
+ sinfo.txrate.he_dcm, sinfo.txrate.he_gi, sinfo.txrate.he_ru_alloc,
+ sinfo.txrate.eht_gi);
+ }
+ wiphy_unlock(hw->wiphy);
+
+ sbuf_finish(&s);
+ sbuf_delete(&s);
+
+ return (0);
+}
+
+static enum ieee80211_sta_rx_bw
+lkpi_cw_to_rx_bw(enum nl80211_chan_width cw)
+{
+ switch (cw) {
+ case NL80211_CHAN_WIDTH_320:
+ return (IEEE80211_STA_RX_BW_320);
+ case NL80211_CHAN_WIDTH_160:
+ case NL80211_CHAN_WIDTH_80P80:
+ return (IEEE80211_STA_RX_BW_160);
+ case NL80211_CHAN_WIDTH_80:
+ return (IEEE80211_STA_RX_BW_80);
+ case NL80211_CHAN_WIDTH_40:
+ return (IEEE80211_STA_RX_BW_40);
+ case NL80211_CHAN_WIDTH_20:
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ return (IEEE80211_STA_RX_BW_20);
+ case NL80211_CHAN_WIDTH_5:
+ case NL80211_CHAN_WIDTH_10:
+ /* Unsupported input. */
+ return (IEEE80211_STA_RX_BW_20);
+ }
+}
+
+static enum nl80211_chan_width
+lkpi_rx_bw_to_cw(enum ieee80211_sta_rx_bw rx_bw)
+{
+ switch (rx_bw) {
+ case IEEE80211_STA_RX_BW_20:
+ return (NL80211_CHAN_WIDTH_20); /* _NOHT */
+ case IEEE80211_STA_RX_BW_40:
+ return (NL80211_CHAN_WIDTH_40);
+ case IEEE80211_STA_RX_BW_80:
+ return (NL80211_CHAN_WIDTH_80);
+ case IEEE80211_STA_RX_BW_160:
+ return (NL80211_CHAN_WIDTH_160); /* 80P80 */
+ case IEEE80211_STA_RX_BW_320:
+ return (NL80211_CHAN_WIDTH_320);
+ }
+}
+
+static void
+lkpi_sync_chanctx_cw_from_rx_bw(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, struct ieee80211_sta *sta)
+{
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ enum ieee80211_sta_rx_bw old_bw;
+ uint32_t changed;
+
+ chanctx_conf = rcu_dereference_protected(vif->bss_conf.chanctx_conf,
+ lockdep_is_held(&hw->wiphy->mtx));
+ if (chanctx_conf == NULL)
+ return;
+
+ old_bw = lkpi_cw_to_rx_bw(chanctx_conf->def.width);
+ if (old_bw == sta->deflink.bandwidth)
+ return;
+
+ chanctx_conf->def.width = lkpi_rx_bw_to_cw(sta->deflink.bandwidth);
+ if (chanctx_conf->def.width == NL80211_CHAN_WIDTH_20 &&
+ !sta->deflink.ht_cap.ht_supported)
+ chanctx_conf->def.width = NL80211_CHAN_WIDTH_20_NOHT;
+
+ chanctx_conf->min_def = chanctx_conf->def;
+
+ vif->bss_conf.chanreq.oper.width = chanctx_conf->def.width;
+
+ changed = IEEE80211_CHANCTX_CHANGE_MIN_WIDTH;
+ changed |= IEEE80211_CHANCTX_CHANGE_WIDTH;
+ lkpi_80211_mo_change_chanctx(hw, chanctx_conf, changed);
+}
#if defined(LKPI_80211_HT)
static void
-lkpi_sta_sync_ht_from_ni(struct ieee80211_sta *sta, struct ieee80211_node *ni, int *ht_rx_nss)
+lkpi_sta_sync_ht_from_ni(struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+ struct ieee80211_node *ni)
{
struct ieee80211vap *vap;
uint8_t *ie;
struct ieee80211_ht_cap *htcap;
int i, rx_nss;
- if ((ni->ni_flags & IEEE80211_NODE_HT) == 0)
+ if ((ni->ni_flags & IEEE80211_NODE_HT) == 0) {
+ sta->deflink.ht_cap.ht_supported = false;
return;
-
- if (IEEE80211_IS_CHAN_HT(ni->ni_chan) &&
- IEEE80211_IS_CHAN_HT40(ni->ni_chan))
- sta->deflink.bandwidth = IEEE80211_STA_RX_BW_40;
+ }
sta->deflink.ht_cap.ht_supported = true;
@@ -188,43 +483,192 @@ lkpi_sta_sync_ht_from_ni(struct ieee80211_sta *sta, struct ieee80211_node *ni, i
sta->deflink.ht_cap.cap = htcap->cap_info;
sta->deflink.ht_cap.mcs = htcap->mcs;
+ /*
+ * 802.11n-2009 20.6 Parameters for HT MCSs gives the mandatory/
+ * optional MCS for Nss=1..4. We need to check the first four
+ * MCS sets from the Rx MCS Bitmask; then there is MCS 32 and
+ * MCS33.. is UEQM.
+ */
rx_nss = 0;
- for (i = 0; i < nitems(htcap->mcs.rx_mask); i++) {
- if (htcap->mcs.rx_mask[i])
+ for (i = 0; i < 4; i++) {
+ if (htcap->mcs.rx_mask[i] != 0)
rx_nss++;
}
- if (ht_rx_nss != NULL)
- *ht_rx_nss = rx_nss;
+ if (rx_nss > 0) {
+ sta->deflink.rx_nss = rx_nss;
+ } else {
+ sta->deflink.ht_cap.ht_supported = false;
+ return;
+ }
- IMPROVE("sta->wme, sta->deflink.agg.max*");
+ if ((sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) != 0 &&
+ IEEE80211_IS_CHAN_HT40(ni->ni_chan))
+ sta->deflink.bandwidth = IEEE80211_STA_RX_BW_40;
+ else
+ sta->deflink.bandwidth = IEEE80211_STA_RX_BW_20;
+
+ IMPROVE("sta->wme");
+
+ if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_MAX_AMSDU)
+ sta->deflink.agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_7935;
+ else
+ sta->deflink.agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_3839;
+ sta->deflink.agg.max_rc_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_BA;
+#ifdef __handled_by_driver__ /* iwlwifi only? actually unused? */
+ for (i = 0; i < nitems(sta.deflink.agg.max_tid_amsdu_len); i++) {
+ sta->deflink.agg.max_tid_amsdu_len[j] = ;
+ }
+#endif
}
#endif
#if defined(LKPI_80211_VHT)
static void
-lkpi_sta_sync_vht_from_ni(struct ieee80211_sta *sta, struct ieee80211_node *ni, int *vht_rx_nss)
-{
-
- if ((ni->ni_flags & IEEE80211_NODE_VHT) == 0)
+lkpi_sta_sync_vht_from_ni(struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+ struct ieee80211_node *ni)
+{
+ enum ieee80211_sta_rx_bw bw;
+ uint32_t width;
+ int rx_nss;
+ uint16_t rx_mcs_map;
+ uint8_t mcs;
+
+ if ((ni->ni_flags & IEEE80211_NODE_VHT) == 0 ||
+ !IEEE80211_IS_CHAN_VHT_5GHZ(ni->ni_chan)) {
+ sta->deflink.vht_cap.vht_supported = false;
return;
+ }
- if (IEEE80211_IS_CHAN_VHT(ni->ni_chan)) {
-#ifdef __notyet__
- if (IEEE80211_IS_CHAN_VHT80P80(ni->ni_chan)) {
- sta->deflink.bandwidth = IEEE80211_STA_RX_BW_160; /* XXX? */
- } else
-#endif
- if (IEEE80211_IS_CHAN_VHT160(ni->ni_chan))
- sta->deflink.bandwidth = IEEE80211_STA_RX_BW_160;
- else if (IEEE80211_IS_CHAN_VHT80(ni->ni_chan))
- sta->deflink.bandwidth = IEEE80211_STA_RX_BW_80;
+ sta->deflink.vht_cap.vht_supported = true;
+
+ sta->deflink.vht_cap.cap = ni->ni_vhtcap;
+ sta->deflink.vht_cap.vht_mcs = ni->ni_vht_mcsinfo;
+
+ /*
+ * If VHT20/40 are selected do not update the bandwidth
+ * from HT but stya on VHT.
+ */
+ if (ni->ni_vht_chanwidth == IEEE80211_VHT_CHANWIDTH_USE_HT)
+ goto skip_bw;
+
+ bw = sta->deflink.bandwidth;
+ width = (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK);
+ switch (width) {
+ /* Deprecated. */
+ case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ:
+ case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ:
+ bw = IEEE80211_STA_RX_BW_160;
+ break;
+ default:
+ /* Check if we do support 160Mhz somehow after all. */
+ if ((sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) != 0)
+ bw = IEEE80211_STA_RX_BW_160;
+ else
+ bw = IEEE80211_STA_RX_BW_80;
}
+ /*
+ * While we can set what is possibly supported we also need to be
+ * on a channel which supports that bandwidth; e.g., we can support
+ * VHT160 but the AP only does VHT80.
+ * Further ni_chan will also have filtered out what we disabled
+ * by configuration.
+ * Once net80211 channel selection is fixed for 802.11-2020 and
+ * VHT160 we can possibly spare ourselves the above.
+ */
+ if (bw == IEEE80211_STA_RX_BW_160 &&
+ !IEEE80211_IS_CHAN_VHT160(ni->ni_chan) &&
+ !IEEE80211_IS_CHAN_VHT80P80(ni->ni_chan))
+ bw = IEEE80211_STA_RX_BW_80;
+ if (bw == IEEE80211_STA_RX_BW_80 &&
+ !IEEE80211_IS_CHAN_VHT80(ni->ni_chan))
+ bw = sta->deflink.bandwidth;
+ sta->deflink.bandwidth = bw;
+skip_bw:
- IMPROVE("VHT sync ni to sta");
- return;
+ rx_nss = 0;
+ rx_mcs_map = sta->deflink.vht_cap.vht_mcs.rx_mcs_map;
+ for (int i = 7; i >= 0; i--) {
+ mcs = rx_mcs_map >> (2 * i);
+ mcs &= 0x3;
+ if (mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED) {
+ rx_nss = i + 1;
+ break;
+ }
+ }
+ if (rx_nss > 0)
+ sta->deflink.rx_nss = rx_nss;
+
+ switch (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_MAX_MPDU_MASK) {
+ case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454:
+ sta->deflink.agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_11454;
+ break;
+ case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991:
+ sta->deflink.agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_7991;
+ break;
+ case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895:
+ default:
+ sta->deflink.agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_3895;
+ break;
+ }
+}
+#endif
+
+static void
+lkpi_sta_sync_from_ni(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, struct ieee80211_node *ni, bool updchnctx)
+{
+
+#if defined(LKPI_80211_HT)
+ lkpi_sta_sync_ht_from_ni(vif, sta, ni);
+#endif
+#if defined(LKPI_80211_VHT)
+ lkpi_sta_sync_vht_from_ni(vif, sta, ni);
+#endif
+
+ /*
+ * Ensure rx_nss is at least 1 as otherwise drivers run into
+ * unexpected problems.
+ */
+ sta->deflink.rx_nss = MAX(1, sta->deflink.rx_nss);
+
+ /*
+ * We are also called from node allocation which net80211
+ * can do even on `ifconfig down`; in that case the chanctx
+ * may still be valid and we get a discrepancy between
+ * sta and chanctx. Thus do not try to update the chanctx
+ * when called from lkpi_lsta_alloc().
+ */
+ if (updchnctx)
+ lkpi_sync_chanctx_cw_from_rx_bw(hw, vif, sta);
}
+
+static uint8_t
+lkpi_get_max_rx_chains(struct ieee80211_node *ni)
+{
+ uint8_t chains;
+#if defined(LKPI_80211_HT) || defined(LKPI_80211_VHT)
+ struct lkpi_sta *lsta;
+ struct ieee80211_sta *sta;
+
+ lsta = ni->ni_drv_data;
+ sta = LSTA_TO_STA(lsta);
+#endif
+
+ chains = 1;
+#if defined(LKPI_80211_HT)
+ IMPROVE("We should factor counting MCS/NSS out for sync and here");
+ if (sta->deflink.ht_cap.ht_supported)
+ chains = MAX(chains, sta->deflink.rx_nss);
#endif
+#if defined(LKPI_80211_VHT)
+ if (sta->deflink.vht_cap.vht_supported)
+ chains = MAX(chains, sta->deflink.rx_nss);
+#endif
+
+ return (chains);
+}
+
static void
lkpi_lsta_dump(struct lkpi_sta *lsta, struct ieee80211_node *ni,
const char *_f, int _l)
@@ -242,7 +686,7 @@ lkpi_lsta_dump(struct lkpi_sta *lsta, struct ieee80211_node *ni,
ieee80211_dump_node(NULL, ni);
printf("\ttxq_task txq len %d mtx\n", mbufq_len(&lsta->txq));
printf("\tkc %p state %d added_to_drv %d in_mgd %d\n",
- lsta->kc, lsta->state, lsta->added_to_drv, lsta->in_mgd);
+ &lsta->kc[0], lsta->state, lsta->added_to_drv, lsta->in_mgd);
#endif
}
@@ -250,13 +694,11 @@ static void
lkpi_lsta_remove(struct lkpi_sta *lsta, struct lkpi_vif *lvif)
{
+ lockdep_assert_wiphy(lsta->hw->wiphy);
- LKPI_80211_LVIF_LOCK(lvif);
- KASSERT(lsta->lsta_entry.tqe_prev != NULL,
- ("%s: lsta %p lsta_entry.tqe_prev %p ni %p\n", __func__,
- lsta, lsta->lsta_entry.tqe_prev, lsta->ni));
- TAILQ_REMOVE(&lvif->lsta_head, lsta, lsta_entry);
- LKPI_80211_LVIF_UNLOCK(lvif);
+ KASSERT(!list_empty(&lsta->lsta_list),
+ ("%s: lsta %p ni %p\n", __func__, lsta, lsta->ni));
+ list_del_init(&lsta->lsta_list);
}
static struct lkpi_sta *
@@ -268,14 +710,13 @@ lkpi_lsta_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN],
struct ieee80211_vif *vif;
struct ieee80211_sta *sta;
int band, i, tid;
- int ht_rx_nss;
- int vht_rx_nss;
lsta = malloc(sizeof(*lsta) + hw->sta_data_size, M_LKPI80211,
M_NOWAIT | M_ZERO);
if (lsta == NULL)
return (NULL);
+ lsta->hw = hw;
lsta->added_to_drv = false;
lsta->state = IEEE80211_STA_NOTEXIST;
/*
@@ -337,29 +778,40 @@ lkpi_lsta_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN],
continue;
for (i = 0; i < supband->n_bitrates; i++) {
-
- IMPROVE("Further supband->bitrates[i]* checks?");
- /* or should we get them from the ni? */
- sta->deflink.supp_rates[band] |= BIT(i);
+ switch (band) {
+ case NL80211_BAND_2GHZ:
+ switch (supband->bitrates[i].bitrate) {
+ case 240: /* 11g only */
+ case 120: /* 11g only */
+ case 110:
+ case 60: /* 11g only */
+ case 55:
+ case 20:
+ case 10:
+ sta->deflink.supp_rates[band] |= BIT(i);
+ break;
+ }
+ break;
+ case NL80211_BAND_5GHZ:
+ switch (supband->bitrates[i].bitrate) {
+ case 240:
+ case 120:
+ case 60:
+ sta->deflink.supp_rates[band] |= BIT(i);
+ break;
+ }
+ break;
+ }
}
}
sta->deflink.smps_mode = IEEE80211_SMPS_OFF;
sta->deflink.bandwidth = IEEE80211_STA_RX_BW_20;
- sta->deflink.rx_nss = 0;
+ sta->deflink.rx_nss = 1;
- ht_rx_nss = 0;
-#if defined(LKPI_80211_HT)
- lkpi_sta_sync_ht_from_ni(sta, ni, &ht_rx_nss);
-#endif
- vht_rx_nss = 0;
-#if defined(LKPI_80211_VHT)
- lkpi_sta_sync_vht_from_ni(sta, ni, &vht_rx_nss);
-#endif
+ lkpi_sta_sync_from_ni(hw, vif, sta, ni, false);
- sta->deflink.rx_nss = MAX(ht_rx_nss, sta->deflink.rx_nss);
- sta->deflink.rx_nss = MAX(vht_rx_nss, sta->deflink.rx_nss);
- IMPROVE("he, ... smps_mode, ..");
+ IMPROVE("he, eht, bw_320, ... smps_mode, ..");
/* Link configuration. */
IEEE80211_ADDR_COPY(sta->deflink.addr, sta->addr);
@@ -367,11 +819,13 @@ lkpi_lsta_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN],
for (i = 1; i < nitems(sta->link); i++) {
IMPROVE("more links; only link[0] = deflink currently.");
}
+ IMPROVE("11be");
+ sta->mlo = false;
/* Deferred TX path. */
LKPI_80211_LSTA_TXQ_LOCK_INIT(lsta);
TASK_INIT(&lsta->txq_task, 0, lkpi_80211_txq_task, lsta);
- mbufq_init(&lsta->txq, IFQ_MAXLEN);
+ mbufq_init(&lsta->txq, 32 * NAPI_POLL_WEIGHT);
lsta->txq_ready = true;
return (lsta);
@@ -538,35 +992,71 @@ lkpi_opmode_to_vif_type(enum ieee80211_opmode opmode)
}
#ifdef LKPI_80211_HW_CRYPTO
-static uint32_t
-lkpi_l80211_to_net80211_cyphers(uint32_t wlan_cipher_suite)
+static const char *
+lkpi_cipher_suite_to_name(uint32_t wlan_cipher_suite)
{
+ switch (wlan_cipher_suite) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ return ("WEP40");
+ case WLAN_CIPHER_SUITE_WEP104:
+ return ("WEP104");
+ case WLAN_CIPHER_SUITE_TKIP:
+ return ("TKIP");
+ case WLAN_CIPHER_SUITE_CCMP:
+ return ("CCMP");
+ case WLAN_CIPHER_SUITE_CCMP_256:
+ return ("CCMP_256");
+ case WLAN_CIPHER_SUITE_GCMP:
+ return ("GCMP");
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ return ("GCMP_256");
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ return ("AES_CMAC");
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+ return ("BIP_CMAC_256");
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ return ("BIP_GMAC_128");
+ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+ return ("BIP_GMAC_256");
+ default:
+ return ("??");
+ }
+}
+static uint32_t
+lkpi_l80211_to_net80211_cyphers(struct ieee80211com *ic,
+ uint32_t wlan_cipher_suite)
+{
switch (wlan_cipher_suite) {
case WLAN_CIPHER_SUITE_WEP40:
return (IEEE80211_CRYPTO_WEP);
+ case WLAN_CIPHER_SUITE_WEP104:
+ return (IEEE80211_CRYPTO_WEP);
case WLAN_CIPHER_SUITE_TKIP:
return (IEEE80211_CRYPTO_TKIP);
case WLAN_CIPHER_SUITE_CCMP:
return (IEEE80211_CRYPTO_AES_CCM);
- case WLAN_CIPHER_SUITE_WEP104:
- return (IEEE80211_CRYPTO_WEP);
- case WLAN_CIPHER_SUITE_AES_CMAC:
+ case WLAN_CIPHER_SUITE_CCMP_256:
+ return (IEEE80211_CRYPTO_AES_CCM_256);
case WLAN_CIPHER_SUITE_GCMP:
+ return (IEEE80211_CRYPTO_AES_GCM_128);
case WLAN_CIPHER_SUITE_GCMP_256:
- case WLAN_CIPHER_SUITE_CCMP_256:
+ return (IEEE80211_CRYPTO_AES_GCM_256);
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ return (IEEE80211_CRYPTO_BIP_CMAC_128);
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+ return (IEEE80211_CRYPTO_BIP_CMAC_256);
case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ return (IEEE80211_CRYPTO_BIP_GMAC_128);
case WLAN_CIPHER_SUITE_BIP_GMAC_256:
- case WLAN_CIPHER_SUITE_BIP_CMAC_256:
- printf("%s: unsupported WLAN Cipher Suite %#08x | %u\n", __func__,
- wlan_cipher_suite >> 8, wlan_cipher_suite & 0xff);
- break;
+ return (IEEE80211_CRYPTO_BIP_GMAC_256);
default:
- printf("%s: unknown WLAN Cipher Suite %#08x | %u\n", __func__,
- wlan_cipher_suite >> 8, wlan_cipher_suite & 0xff);
+ ic_printf(ic, "%s: unknown WLAN Cipher Suite %#08x | %u (%s)\n",
+ __func__,
+ wlan_cipher_suite >> 8, wlan_cipher_suite & 0xff,
+ lkpi_cipher_suite_to_name(wlan_cipher_suite));
+ return (0);
}
-
- return (0);
}
static uint32_t
@@ -574,18 +1064,42 @@ lkpi_net80211_to_l80211_cipher_suite(uint32_t cipher, uint8_t keylen)
{
switch (cipher) {
- case IEEE80211_CIPHER_TKIP:
- return (WLAN_CIPHER_SUITE_TKIP);
- case IEEE80211_CIPHER_AES_CCM:
- return (WLAN_CIPHER_SUITE_CCMP);
case IEEE80211_CIPHER_WEP:
- if (keylen < 8)
+ if (keylen == (40/NBBY))
return (WLAN_CIPHER_SUITE_WEP40);
- else
+ else if (keylen == (104/NBBY))
return (WLAN_CIPHER_SUITE_WEP104);
+ else {
+ printf("%s: WEP with unsupported keylen %d\n",
+ __func__, keylen * NBBY);
+ return (0);
+ }
break;
+ case IEEE80211_CIPHER_TKIP:
+ return (WLAN_CIPHER_SUITE_TKIP);
+ case IEEE80211_CIPHER_AES_CCM:
+ return (WLAN_CIPHER_SUITE_CCMP);
+ case IEEE80211_CIPHER_AES_CCM_256:
+ return (WLAN_CIPHER_SUITE_CCMP_256);
+ case IEEE80211_CIPHER_AES_GCM_128:
+ return (WLAN_CIPHER_SUITE_GCMP);
+ case IEEE80211_CIPHER_AES_GCM_256:
+ return (WLAN_CIPHER_SUITE_GCMP_256);
+ case IEEE80211_CIPHER_BIP_CMAC_128:
+ return (WLAN_CIPHER_SUITE_AES_CMAC);
+ case IEEE80211_CIPHER_BIP_CMAC_256:
+ return (WLAN_CIPHER_SUITE_BIP_CMAC_256);
+ case IEEE80211_CIPHER_BIP_GMAC_128:
+ return (WLAN_CIPHER_SUITE_BIP_GMAC_128);
+ case IEEE80211_CIPHER_BIP_GMAC_256:
+ return (WLAN_CIPHER_SUITE_BIP_GMAC_256);
+
case IEEE80211_CIPHER_AES_OCB:
case IEEE80211_CIPHER_TKIPMIC:
+ /*
+ * TKIP w/ hw MIC support
+ * (gone wrong; should really be a crypto flag in net80211).
+ */
case IEEE80211_CIPHER_CKIP:
case IEEE80211_CIPHER_NONE:
printf("%s: unsupported cipher %#010x\n", __func__, cipher);
@@ -708,31 +1222,242 @@ linuxkpi_ieee80211_get_channel(struct wiphy *wiphy, uint32_t freq)
#ifdef LKPI_80211_HW_CRYPTO
static int
-_lkpi_iv_key_set_delete(struct ieee80211vap *vap, const struct ieee80211_key *k,
- enum set_key_cmd cmd)
+lkpi_sta_del_keys(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct lkpi_sta *lsta)
+{
+ int error;
+
+ if (!lkpi_hwcrypto)
+ return (0);
+
+ lockdep_assert_wiphy(hw->wiphy);
+ ieee80211_ref_node(lsta->ni);
+
+ error = 0;
+ for (ieee80211_keyix keyix = 0; keyix < nitems(lsta->kc); keyix++) {
+ struct ieee80211_key_conf *kc;
+ int err;
+
+ if (lsta->kc[keyix] == NULL)
+ continue;
+ kc = lsta->kc[keyix];
+
+#ifdef LINUXKPI_DEBUG_80211
+ if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO)
+ ic_printf(lsta->ni->ni_ic, "%d %lu %s: running set_key cmd %d(%s) for "
+ "sta %6D: keyidx %u hw_key_idx %u flags %b\n",
+ curthread->td_tid, jiffies, __func__,
+ DISABLE_KEY, "DISABLE", lsta->sta.addr, ":",
+ kc->keyidx, kc->hw_key_idx, kc->flags, IEEE80211_KEY_FLAG_BITS);
+#endif
+
+ err = lkpi_80211_mo_set_key(hw, DISABLE_KEY, vif,
+ LSTA_TO_STA(lsta), kc);
+ if (err != 0) {
+ ic_printf(lsta->ni->ni_ic, "%d %lu %s: set_key cmd %d(%s) for "
+ "sta %6D failed: %d\n", curthread->td_tid, jiffies, __func__,
+ DISABLE_KEY, "DISABLE", lsta->sta.addr, ":", err);
+ error++;
+
+ /*
+ * If we free the key here we will never be able to get it
+ * removed from the driver/fw which will likely make us
+ * crash (firmware).
+ */
+ continue;
+ }
+#ifdef LINUXKPI_DEBUG_80211
+ if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO)
+ ic_printf(lsta->ni->ni_ic, "%d %lu %s: set_key cmd %d(%s) for "
+ "sta %6D succeeded: keyidx %u hw_key_idx %u flags %b\n",
+ curthread->td_tid, jiffies, __func__,
+ DISABLE_KEY, "DISABLE", lsta->sta.addr, ":",
+ kc->keyidx, kc->hw_key_idx, kc->flags, IEEE80211_KEY_FLAG_BITS);
+#endif
+
+ lsta->kc[keyix] = NULL;
+ free(kc, M_LKPI80211);
+ }
+ ieee80211_free_node(lsta->ni);
+ return (error);
+}
+
+/* XXX-BZ one day we should replace this iterating over VIFs, or node list? */
+/* See also lkpi_sta_del_keys() these days. */
+static int
+lkpi_iv_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k)
{
struct ieee80211com *ic;
struct lkpi_hw *lhw;
struct ieee80211_hw *hw;
struct lkpi_vif *lvif;
+ struct lkpi_sta *lsta;
struct ieee80211_vif *vif;
struct ieee80211_sta *sta;
struct ieee80211_node *ni;
struct ieee80211_key_conf *kc;
int error;
- /* XXX TODO Check (k->wk_flags & IEEE80211_KEY_SWENCRYPT) and don't upload to driver/hw? */
-
ic = vap->iv_ic;
lhw = ic->ic_softc;
hw = LHW_TO_HW(lhw);
lvif = VAP_TO_LVIF(vap);
+
+ /*
+ * Make sure we do not make it here without going through
+ * lkpi_iv_key_update_begin() first.
+ */
+ lockdep_assert_wiphy(hw->wiphy);
+
+ 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);
+ 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);
+ }
+ sta = LSTA_TO_STA(lsta);
+
+ if (lsta->kc[k->wk_keyix] == NULL) {
+#ifdef LINUXKPI_DEBUG_80211
+ if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO)
+ ic_printf(ic, "%d %lu %s: sta %6D and no key information, "
+ "keyidx %u wk_macaddr %6D; returning success\n",
+ curthread->td_tid, jiffies, __func__, sta->addr, ":",
+ k->wk_keyix, k->wk_macaddr, ":");
+#endif
+ ieee80211_free_node(ni);
+ return (1);
+ }
+ kc = lsta->kc[k->wk_keyix];
+
+#ifdef LINUXKPI_DEBUG_80211
+ if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO)
+ ic_printf(ic, "%d %lu %s: running set_key cmd %d(%s) for sta %6D: "
+ "keyidx %u hw_key_idx %u flags %b\n",
+ curthread->td_tid, jiffies, __func__,
+ DISABLE_KEY, "DISABLE", sta->addr, ":",
+ kc->keyidx, kc->hw_key_idx, kc->flags, IEEE80211_KEY_FLAG_BITS);
+#endif
+
vif = LVIF_TO_VIF(lvif);
+ error = lkpi_80211_mo_set_key(hw, DISABLE_KEY, vif, sta, kc);
+ if (error != 0) {
+ ic_printf(ic, "%d %lu %s: set_key cmd %d(%s) for sta %6D failed: %d\n",
+ curthread->td_tid, jiffies, __func__,
+ DISABLE_KEY, "DISABLE", sta->addr, ":", error);
+ error = 0;
+ goto out;
+ }
+
+#ifdef LINUXKPI_DEBUG_80211
+ if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO)
+ ic_printf(ic, "%d %lu %s: set_key cmd %d(%s) for sta %6D succeeded: "
+ "keyidx %u hw_key_idx %u flags %b\n",
+ curthread->td_tid, jiffies, __func__,
+ DISABLE_KEY, "DISABLE", sta->addr, ":",
+ kc->keyidx, kc->hw_key_idx, kc->flags, IEEE80211_KEY_FLAG_BITS);
+#endif
+ lsta->kc[k->wk_keyix] = NULL;
+ free(kc, M_LKPI80211);
+ error = 1;
+out:
+ ieee80211_free_node(ni);
+ return (error);
+}
+
+static int
+lkpi_iv_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k)
+{
+ struct ieee80211com *ic;
+ struct lkpi_hw *lhw;
+ struct ieee80211_hw *hw;
+ struct lkpi_vif *lvif;
+ struct lkpi_sta *lsta;
+ struct ieee80211_vif *vif;
+ struct ieee80211_sta *sta;
+ struct ieee80211_node *ni;
+ struct ieee80211_key_conf *kc;
+ uint32_t lcipher;
+ uint16_t exp_flags;
+ uint8_t keylen;
+ int error;
+
+ ic = vap->iv_ic;
+ lhw = ic->ic_softc;
+ hw = LHW_TO_HW(lhw);
+
+ /*
+ * Make sure we do not make it here without going through
+ * lkpi_iv_key_update_begin() first.
+ */
+ lockdep_assert_wiphy(hw->wiphy);
+
+ 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);
+ 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);
+ }
+ sta = LSTA_TO_STA(lsta);
- memset(&kc, 0, sizeof(kc));
- kc = malloc(sizeof(*kc) + k->wk_keylen, M_LKPI80211, M_WAITOK | M_ZERO);
- kc->cipher = lkpi_net80211_to_l80211_cipher_suite(
+ keylen = k->wk_keylen;
+ lcipher = lkpi_net80211_to_l80211_cipher_suite(
k->wk_cipher->ic_cipher, k->wk_keylen);
+ switch (lcipher) {
+ case WLAN_CIPHER_SUITE_TKIP:
+ keylen += 2 * k->wk_cipher->ic_miclen;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_GCMP:
+ break;
+ default:
+ ic_printf(ic, "%s: CIPHER SUITE %#x (%s) not supported\n",
+ __func__, lcipher, lkpi_cipher_suite_to_name(lcipher));
+ IMPROVE();
+ ieee80211_free_node(ni);
+ return (0);
+ }
+
+ if (lsta->kc[k->wk_keyix] != NULL) {
+ IMPROVE("Still in firmware? Del first. Can we assert this cannot happen?");
+ ic_printf(ic, "%s: sta %6D found with key information\n",
+ __func__, sta->addr, ":");
+ kc = lsta->kc[k->wk_keyix];
+ lsta->kc[k->wk_keyix] = NULL;
+ free(kc, M_LKPI80211);
+ kc = NULL; /* safeguard */
+ }
+
+ kc = malloc(sizeof(*kc) + keylen, M_LKPI80211, M_WAITOK | M_ZERO);
+ kc->_k = k; /* Save the pointer to net80211. */
+ kc->cipher = lcipher;
kc->keyidx = k->wk_keyix;
#if 0
kc->hw_key_idx = /* set by hw and needs to be passed for TX */;
@@ -741,51 +1466,237 @@ _lkpi_iv_key_set_delete(struct ieee80211vap *vap, const struct ieee80211_key *k,
kc->keylen = k->wk_keylen;
memcpy(kc->key, k->wk_key, k->wk_keylen);
+ if (k->wk_flags & (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV))
+ kc->flags |= IEEE80211_KEY_FLAG_PAIRWISE;
+ if (k->wk_flags & IEEE80211_KEY_GROUP)
+ kc->flags &= ~IEEE80211_KEY_FLAG_PAIRWISE;
+
+ kc->iv_len = k->wk_cipher->ic_header;
+ kc->icv_len = k->wk_cipher->ic_trailer;
+
switch (kc->cipher) {
+ case WLAN_CIPHER_SUITE_TKIP:
+ memcpy(kc->key + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY, k->wk_txmic, k->wk_cipher->ic_miclen);
+ memcpy(kc->key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY, k->wk_rxmic, k->wk_cipher->ic_miclen);
+ break;
case WLAN_CIPHER_SUITE_CCMP:
- kc->iv_len = k->wk_cipher->ic_header;
- kc->icv_len = k->wk_cipher->ic_trailer;
+ case WLAN_CIPHER_SUITE_GCMP:
break;
- case WLAN_CIPHER_SUITE_TKIP:
default:
+ /* currently UNREACH */
IMPROVE();
- return (0);
+ break;
};
+ lsta->kc[k->wk_keyix] = kc;
- ni = vap->iv_bss;
- sta = ieee80211_find_sta(vif, ni->ni_bssid);
- if (sta != NULL) {
- struct lkpi_sta *lsta;
-
- lsta = STA_TO_LSTA(sta);
- lsta->kc = kc;
- }
+#ifdef LINUXKPI_DEBUG_80211
+ if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO)
+ ic_printf(ic, "%d %lu %s: running set_key cmd %d(%s) for sta %6D: "
+ "kc %p keyidx %u hw_key_idx %u keylen %u flags %b\n",
+ curthread->td_tid, jiffies, __func__,
+ SET_KEY, "SET", sta->addr, ":", kc, kc->keyidx, kc->hw_key_idx,
+ kc->keylen, kc->flags, IEEE80211_KEY_FLAG_BITS);
+#endif
- error = lkpi_80211_mo_set_key(hw, cmd, vif, sta, kc);
+ lvif = VAP_TO_LVIF(vap);
+ vif = LVIF_TO_VIF(lvif);
+ error = lkpi_80211_mo_set_key(hw, SET_KEY, vif, sta, kc);
if (error != 0) {
- /* XXX-BZ leaking kc currently */
- ic_printf(ic, "%s: set_key failed: %d\n", __func__, error);
+ ic_printf(ic, "%d %lu %s: set_key cmd %d(%s) for sta %6D failed: %d\n",
+ curthread->td_tid, jiffies, __func__,
+ SET_KEY, "SET", sta->addr, ":", error);
+ lsta->kc[k->wk_keyix] = NULL;
+ free(kc, M_LKPI80211);
+ ieee80211_free_node(ni);
return (0);
- } else {
- ic_printf(ic, "%s: set_key succeeded: keyidx %u hw_key_idx %u "
- "flags %#10x\n", __func__,
- kc->keyidx, kc->hw_key_idx, kc->flags);
- return (1);
}
+
+#ifdef LINUXKPI_DEBUG_80211
+ if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO)
+ ic_printf(ic, "%d %lu %s: set_key cmd %d(%s) for sta %6D succeeded: "
+ "kc %p keyidx %u hw_key_idx %u flags %b\n",
+ curthread->td_tid, jiffies, __func__,
+ SET_KEY, "SET", sta->addr, ":",
+ kc, kc->keyidx, kc->hw_key_idx, kc->flags, IEEE80211_KEY_FLAG_BITS);
+#endif
+
+ exp_flags = 0;
+ switch (kc->cipher) {
+ case WLAN_CIPHER_SUITE_TKIP:
+ exp_flags = (IEEE80211_KEY_FLAG_PAIRWISE |
+ IEEE80211_KEY_FLAG_PUT_IV_SPACE |
+ IEEE80211_KEY_FLAG_GENERATE_MMIC |
+ IEEE80211_KEY_FLAG_PUT_MIC_SPACE);
+#define TKIP_INVAL_COMBINATION \
+ (IEEE80211_KEY_FLAG_PUT_MIC_SPACE|IEEE80211_KEY_FLAG_GENERATE_MMIC)
+ if ((kc->flags & TKIP_INVAL_COMBINATION) == TKIP_INVAL_COMBINATION) {
+ ic_printf(ic, "%s: SET_KEY for %s returned invalid "
+ "combination %b\n", __func__,
+ lkpi_cipher_suite_to_name(kc->cipher),
+ kc->flags, IEEE80211_KEY_FLAG_BITS);
+ }
+#undef TKIP_INVAL_COMBINATION
+#ifdef __notyet__
+ /* Do flags surgery; special see linuxkpi_ieee80211_ifattach(). */
+ if ((kc->flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) != 0) {
+ k->wk_flags &= ~(IEEE80211_KEY_NOMICMGT|IEEE80211_KEY_NOMIC);
+ k->wk_flags |= IEEE80211_KEY_SWMIC;
+ ic->ic_cryptocaps &= ~IEEE80211_CRYPTO_TKIPMIC
+ }
+#endif
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_GCMP:
+ exp_flags = (IEEE80211_KEY_FLAG_PAIRWISE |
+ IEEE80211_KEY_FLAG_PUT_IV_SPACE |
+ IEEE80211_KEY_FLAG_GENERATE_IV |
+ IEEE80211_KEY_FLAG_GENERATE_IV_MGMT | /* Only needs IV geeration for MGMT frames. */
+ IEEE80211_KEY_FLAG_SW_MGMT_TX); /* MFP in software */
+ break;
+ }
+ if ((kc->flags & ~exp_flags) != 0)
+ ic_printf(ic, "%s: SET_KEY for %s returned unexpected key flags: "
+ " %#06x & ~%#06x = %b\n", __func__,
+ lkpi_cipher_suite_to_name(kc->cipher), kc->flags, exp_flags,
+ (kc->flags & ~exp_flags), IEEE80211_KEY_FLAG_BITS);
+
+#ifdef __notyet__
+ /* Do flags surgery. */
+ if ((kc->flags & IEEE80211_KEY_FLAG_GENERATE_IV_MGMT) == 0)
+ k->wk_flags |= IEEE80211_KEY_NOIVMGT;
+ if ((kc->flags & IEEE80211_KEY_FLAG_GENERATE_IV) == 0)
+ k->wk_flags |= IEEE80211_KEY_NOIV;
+#endif
+
+ ieee80211_free_node(ni);
+ return (1);
}
-static int
-lkpi_iv_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k)
+static void
+lkpi_iv_key_update_begin(struct ieee80211vap *vap)
{
+ struct ieee80211_node_table *nt;
+ struct ieee80211com *ic;
+ struct lkpi_hw *lhw;
+ struct ieee80211_hw *hw;
+ struct lkpi_vif *lvif;
+ struct ieee80211_node *ni;
+ bool icislocked, ntislocked;
+
+ ic = vap->iv_ic;
+ lhw = ic->ic_softc;
+ hw = LHW_TO_HW(lhw);
+ lvif = VAP_TO_LVIF(vap);
+ nt = &ic->ic_sta;
+
+ icislocked = IEEE80211_IS_LOCKED(ic);
+ ntislocked = IEEE80211_NODE_IS_LOCKED(nt);
+
+#ifdef LINUXKPI_DEBUG_80211
+ if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO)
+ ic_printf(ic, "%d %lu %s: vap %p ic %p %slocked nt %p %slocked "
+ "lvif ic_unlocked %d nt_unlocked %d\n",
+ curthread->td_tid, jiffies, __func__, vap,
+ ic, icislocked ? "" : "un", nt, ntislocked ? "" : "un",
+ lvif->ic_unlocked, lvif->nt_unlocked);
+#endif
- /* XXX-BZ one day we should replace this iterating over VIFs, or node list? */
- return (_lkpi_iv_key_set_delete(vap, k, DISABLE_KEY));
+ /*
+ * This is inconsistent net80211 locking to be fixed one day.
+ */
+ /* Try to make sure the node does not go away while possibly unlocked. */
+ ni = NULL;
+ if (icislocked || ntislocked) {
+ if (vap->iv_bss != NULL)
+ ni = ieee80211_ref_node(vap->iv_bss);
+ }
+
+ if (icislocked)
+ IEEE80211_UNLOCK(ic);
+ if (ntislocked)
+ IEEE80211_NODE_UNLOCK(nt);
+
+ wiphy_lock(hw->wiphy);
+
+ KASSERT(lvif->key_update_iv_bss == NULL, ("%s: key_update_iv_bss not NULL %p",
+ __func__, lvif->key_update_iv_bss));
+ lvif->key_update_iv_bss = ni;
+
+ /*
+ * ic/nt_unlocked could be a bool given we are under the lock and there
+ * must only be a single thread.
+ * In case anything in the future disturbs the order the refcnt will
+ * help us catching problems a lot easier.
+ */
+ if (icislocked)
+ refcount_acquire(&lvif->ic_unlocked);
+ if (ntislocked)
+ refcount_acquire(&lvif->nt_unlocked);
+
+ /*
+ * Stop the queues while doing key updates.
+ */
+ ieee80211_stop_queues(hw);
}
-static int
-lkpi_iv_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k)
+
+static void
+lkpi_iv_key_update_end(struct ieee80211vap *vap)
{
+ struct ieee80211_node_table *nt;
+ struct ieee80211com *ic;
+ struct lkpi_hw *lhw;
+ struct ieee80211_hw *hw;
+ struct lkpi_vif *lvif;
+ bool icislocked, ntislocked;
+
+ ic = vap->iv_ic;
+ lhw = ic->ic_softc;
+ hw = LHW_TO_HW(lhw);
+ lvif = VAP_TO_LVIF(vap);
+ nt = &ic->ic_sta;
+
+ /*
+ * Re-enabled the queues after the key update.
+ */
+ lkpi_ieee80211_wake_queues_locked(hw);
+
+ icislocked = IEEE80211_IS_LOCKED(ic);
+ MPASS(!icislocked);
+ ntislocked = IEEE80211_NODE_IS_LOCKED(nt);
+ MPASS(!ntislocked);
- return (_lkpi_iv_key_set_delete(vap, k, SET_KEY));
+#ifdef LINUXKPI_DEBUG_80211
+ if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO)
+ ic_printf(ic, "%d %lu %s: vap %p ic %p %slocked nt %p %slocked "
+ "lvif ic_unlocked %d nt_unlocked %d\n",
+ curthread->td_tid, jiffies, __func__, vap,
+ ic, icislocked ? "" : "un", nt, ntislocked ? "" : "un",
+ lvif->ic_unlocked, lvif->nt_unlocked);
+#endif
+
+ /*
+ * Check under lock; see comment in lkpi_iv_key_update_begin().
+ * In case the refcnt gets out of sync locking in net80211 will
+ * quickly barf as well (trying to unlock a lock not held).
+ */
+ icislocked = refcount_release_if_last(&lvif->ic_unlocked);
+ ntislocked = refcount_release_if_last(&lvif->nt_unlocked);
+
+ if (lvif->key_update_iv_bss != NULL) {
+ ieee80211_free_node(lvif->key_update_iv_bss);
+ lvif->key_update_iv_bss = NULL;
+ }
+
+ wiphy_unlock(hw->wiphy);
+
+ /*
+ * This is inconsistent net80211 locking to be fixed one day.
+ * ic before nt to avoid a LOR.
+ */
+ if (icislocked)
+ IEEE80211_LOCK(ic);
+ if (ntislocked)
+ IEEE80211_NODE_LOCK(nt);
}
#endif
@@ -894,14 +1805,14 @@ lkpi_update_dtim_tsf(struct ieee80211_vif *vif, struct ieee80211_node *ni,
if (linuxkpi_debug_80211 & D80211_TRACE)
printf("%s:%d [%s:%d] assoc %d aid %d beacon_int %u "
"dtim_period %u sync_dtim_count %u sync_tsf %ju "
- "sync_device_ts %u bss_changed %#08x\n",
+ "sync_device_ts %u bss_changed %#010jx\n",
__func__, __LINE__, _f, _l,
vif->cfg.assoc, vif->cfg.aid,
vif->bss_conf.beacon_int, vif->bss_conf.dtim_period,
vif->bss_conf.sync_dtim_count,
(uintmax_t)vif->bss_conf.sync_tsf,
vif->bss_conf.sync_device_ts,
- bss_changed);
+ (uintmax_t)bss_changed);
#endif
if (vif->bss_conf.beacon_int != ni->ni_intval) {
@@ -925,14 +1836,14 @@ lkpi_update_dtim_tsf(struct ieee80211_vif *vif, struct ieee80211_node *ni,
if (linuxkpi_debug_80211 & D80211_TRACE)
printf("%s:%d [%s:%d] assoc %d aid %d beacon_int %u "
"dtim_period %u sync_dtim_count %u sync_tsf %ju "
- "sync_device_ts %u bss_changed %#08x\n",
+ "sync_device_ts %u bss_changed %#010jx\n",
__func__, __LINE__, _f, _l,
vif->cfg.assoc, vif->cfg.aid,
vif->bss_conf.beacon_int, vif->bss_conf.dtim_period,
vif->bss_conf.sync_dtim_count,
(uintmax_t)vif->bss_conf.sync_tsf,
vif->bss_conf.sync_device_ts,
- bss_changed);
+ (uintmax_t)bss_changed);
#endif
return (bss_changed);
@@ -954,10 +1865,10 @@ lkpi_stop_hw_scan(struct lkpi_hw *lhw, struct ieee80211_vif *vif)
hw = LHW_TO_HW(lhw);
IEEE80211_UNLOCK(lhw->ic);
- LKPI_80211_LHW_LOCK(lhw);
+ wiphy_lock(hw->wiphy);
/* Need to cancel the scan. */
lkpi_80211_mo_cancel_hw_scan(hw, vif);
- LKPI_80211_LHW_UNLOCK(lhw);
+ wiphy_unlock(hw->wiphy);
/* Need to make sure we see ieee80211_scan_completed. */
LKPI_80211_LHW_SCAN_LOCK(lhw);
@@ -993,33 +1904,37 @@ lkpi_hw_conf_idle(struct ieee80211_hw *hw, bool new)
}
}
-static void
+static enum ieee80211_bss_changed
lkpi_disassoc(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
struct lkpi_hw *lhw)
{
+ enum ieee80211_bss_changed changed;
+
+ changed = 0;
sta->aid = 0;
if (vif->cfg.assoc) {
- struct ieee80211_hw *hw;
- enum ieee80211_bss_changed changed;
lhw->update_mc = true;
lkpi_update_mcast_filter(lhw->ic, true);
- changed = 0;
vif->cfg.assoc = false;
vif->cfg.aid = 0;
changed |= BSS_CHANGED_ASSOC;
- /*
- * This will remove the sta from firmware for iwlwifi.
- * So confusing that they use state and flags and ... ^%$%#%$^.
- */
IMPROVE();
- hw = LHW_TO_HW(lhw);
- lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf,
- changed);
- lkpi_hw_conf_idle(hw, true);
+ /*
+ * Executing the bss_info_changed(BSS_CHANGED_ASSOC) with
+ * assoc = false right away here will remove the sta from
+ * firmware for iwlwifi.
+ * We no longer do this but only return the BSS_CHNAGED value.
+ * 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.
+ */
}
+
+ return (changed);
}
static void
@@ -1057,6 +1972,83 @@ lkpi_wake_tx_queues(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
}
}
+/*
+ * On the way down from RUN -> ASSOC -> AUTH we may send a DISASSOC or DEAUTH
+ * packet. The problem is that the state machine functions tend to hold the
+ * LHW lock which will prevent lkpi_80211_txq_tx_one() from sending the packet.
+ * We call this after dropping the ic lock and before acquiring the LHW lock.
+ * we make sure no further packets are queued and if they are queued the task
+ * will finish or be cancelled. At the end if a packet is left we manually
+ * send it. scan_to_auth() would re-enable sending if the lsta would be
+ * re-used.
+ */
+static void
+lkpi_80211_flush_tx(struct lkpi_hw *lhw, struct lkpi_sta *lsta)
+{
+ struct ieee80211_hw *hw;
+ struct mbufq mq;
+ struct mbuf *m;
+ int len;
+
+ /* There is no lockdep_assert_not_held_wiphy(). */
+ hw = LHW_TO_HW(lhw);
+ lockdep_assert_not_held(&hw->wiphy->mtx);
+
+ /* Do not accept any new packets until scan_to_auth or lsta_free(). */
+ LKPI_80211_LSTA_TXQ_LOCK(lsta);
+ lsta->txq_ready = false;
+ LKPI_80211_LSTA_TXQ_UNLOCK(lsta);
+
+ while (taskqueue_cancel(taskqueue_thread, &lsta->txq_task, NULL) != 0)
+ taskqueue_drain(taskqueue_thread, &lsta->txq_task);
+
+ LKPI_80211_LSTA_TXQ_LOCK(lsta);
+ len = mbufq_len(&lsta->txq);
+ if (len <= 0) {
+ LKPI_80211_LSTA_TXQ_UNLOCK(lsta);
+ return;
+ }
+
+ mbufq_init(&mq, IFQ_MAXLEN);
+ mbufq_concat(&mq, &lsta->txq);
+ LKPI_80211_LSTA_TXQ_UNLOCK(lsta);
+
+ m = mbufq_dequeue(&mq);
+ while (m != NULL) {
+ lkpi_80211_txq_tx_one(lsta, m);
+ m = mbufq_dequeue(&mq);
+ }
+}
+
+
+static void
+lkpi_remove_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ struct lkpi_chanctx *lchanctx;
+
+ chanctx_conf = rcu_dereference_protected(vif->bss_conf.chanctx_conf,
+ lockdep_is_held(&hw->wiphy->mtx));
+
+ if (chanctx_conf == NULL)
+ return;
+
+ /* Remove vif context. */
+ lkpi_80211_mo_unassign_vif_chanctx(hw, vif, &vif->bss_conf, chanctx_conf);
+
+ lkpi_hw_conf_idle(hw, true);
+
+ /* Remove chan ctx. */
+ lkpi_80211_mo_remove_chanctx(hw, chanctx_conf);
+
+ /* Cleanup. */
+ rcu_assign_pointer(vif->bss_conf.chanctx_conf, NULL);
+ lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf);
+ list_del(&lchanctx->entry);
+ free(lchanctx, M_LKPI80211);
+}
+
+
/* -------------------------------------------------------------------------- */
static int
@@ -1074,7 +2066,7 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int
{
struct linuxkpi_ieee80211_channel *chan;
struct lkpi_chanctx *lchanctx;
- struct ieee80211_chanctx_conf *conf;
+ struct ieee80211_chanctx_conf *chanctx_conf;
struct lkpi_hw *lhw;
struct ieee80211_hw *hw;
struct lkpi_vif *lvif;
@@ -1085,6 +2077,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;
/*
* In here we use vap->iv_bss until lvif->lvif_bss is set.
@@ -1131,64 +2124,72 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int
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);
+ ieee80211_free_node(ni); /* Error handling for the local ni. */
return (EBUSY);
}
LKPI_80211_LVIF_UNLOCK(lvif);
IEEE80211_UNLOCK(vap->iv_ic);
- LKPI_80211_LHW_LOCK(lhw);
+ wiphy_lock(hw->wiphy);
/* Add chanctx (or if exists, change it). */
- if (vif->chanctx_conf != NULL) {
- conf = vif->chanctx_conf;
- lchanctx = CHANCTX_CONF_TO_LCHANCTX(conf);
+ 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);
- conf = &lchanctx->conf;
+ chanctx_conf = &lchanctx->chanctx_conf;
}
- conf->rx_chains_dynamic = 1;
- conf->rx_chains_static = 1;
- conf->radar_enabled =
+ chanctx_conf->rx_chains_static = 1;
+ chanctx_conf->rx_chains_dynamic = 1;
+ chanctx_conf->radar_enabled =
(chan->flags & IEEE80211_CHAN_RADAR) ? true : false;
- conf->def.chan = chan;
- conf->def.width = NL80211_CHAN_WIDTH_20_NOHT;
- conf->def.center_freq1 = chan->center_freq;
- conf->def.center_freq2 = 0;
+ 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)) {
- conf->def.width = NL80211_CHAN_WIDTH_40;
- } else
- conf->def.width = NL80211_CHAN_WIDTH_20;
+ 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;
}
#endif
#ifdef LKPI_80211_VHT
- if (IEEE80211_IS_CHAN_VHT(ni->ni_chan)) {
-#ifdef __notyet__
- if (IEEE80211_IS_CHAN_VHT80P80(ni->ni_chan)) {
- conf->def.width = NL80211_CHAN_WIDTH_80P80;
- conf->def.center_freq2 = 0; /* XXX */
- } else
-#endif
- if (IEEE80211_IS_CHAN_VHT160(ni->ni_chan))
- conf->def.width = NL80211_CHAN_WIDTH_160;
+ 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))
- conf->def.width = NL80211_CHAN_WIDTH_80;
+ chanctx_conf->def.width = NL80211_CHAN_WIDTH_80;
}
#endif
+ chanctx_conf->rx_chains_dynamic = lkpi_get_max_rx_chains(ni);
/* Responder ... */
- conf->min_def.chan = chan;
- conf->min_def.width = NL80211_CHAN_WIDTH_20_NOHT;
- conf->min_def.center_freq1 = chan->center_freq;
- conf->min_def.center_freq2 = 0;
- IMPROVE("currently 20_NOHT min_def only");
+#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;
@@ -1208,49 +2209,43 @@ 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->chanctx_conf != NULL) {
+ 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, conf, changed);
+ lkpi_80211_mo_change_chanctx(hw, chanctx_conf, changed);
} else {
- error = lkpi_80211_mo_add_chanctx(hw, conf);
+ error = lkpi_80211_mo_add_chanctx(hw, chanctx_conf);
if (error == 0 || error == EOPNOTSUPP) {
- vif->bss_conf.chandef.chan = conf->def.chan;
- vif->bss_conf.chandef.width = conf->def.width;
- vif->bss_conf.chandef.center_freq1 =
- conf->def.center_freq1;
-#ifdef LKPI_80211_HT
- if (vif->bss_conf.chandef.width == NL80211_CHAN_WIDTH_40) {
- /* Note: it is 10 not 20. */
- if (IEEE80211_IS_CHAN_HT40U(ni->ni_chan))
- vif->bss_conf.chandef.center_freq1 += 10;
- else if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan))
- vif->bss_conf.chandef.center_freq1 -= 10;
- }
-#endif
- vif->bss_conf.chandef.center_freq2 =
- conf->def.center_freq2;
+ 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;
}
- vif->bss_conf.chanctx_conf = 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, conf);
+ &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, conf);
- lchanctx = CHANCTX_CONF_TO_LCHANCTX(conf);
+ 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;
}
@@ -1270,29 +2265,8 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int
__func__, ni, ni->ni_drv_data));
lsta = ni->ni_drv_data;
- LKPI_80211_LVIF_LOCK(lvif);
- /* Re-check given (*iv_update_bss) could have happened. */
- /* XXX-BZ KASSERT later? or deal as error? */
- if (lvif->lvif_bss_synched || 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, ni %p lsta %p\n", __func__, __LINE__,
- lvif, vap, vap->iv_bss, lvif->lvif_bss,
- (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
- lvif->lvif_bss_synched, ni, lsta);
-
- /*
- * Reference the ni for this cache of lsta/ni on lvif->lvif_bss
- * essentially out lsta version of the iv_bss.
- * Do NOT use iv_bss here anymore as that may have diverged from our
- * function local ni already and would lead to inconsistencies.
- */
- ieee80211_ref_node(ni);
- lvif->lvif_bss = lsta;
- lvif->lvif_bss_synched = true;
-
/* Insert the [l]sta into the list of known stations. */
- TAILQ_INSERT_TAIL(&lvif->lsta_head, lsta, lsta_entry);
- LKPI_80211_LVIF_UNLOCK(lvif);
+ list_add_tail(&lsta->lsta_list, &lvif->lsta_list);
/* Add (or adjust) sta and change state (from NOTEXIST) to NONE. */
KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
@@ -1338,11 +2312,67 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int
* (ideally we'd do that on a callback for something else ...)
*/
+ wiphy_unlock(hw->wiphy);
+ IEEE80211_LOCK(vap->iv_ic);
+
+ LKPI_80211_LVIF_LOCK(lvif);
+ /* Re-check given (*iv_update_bss) could have happened while we were unlocked. */
+ if (lvif->lvif_bss_synched || lvif->lvif_bss != NULL ||
+ lsta->ni != vap->iv_bss)
+ ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p "
+ "lvif_bss->ni %p synched %d, ni %p lsta %p\n", __func__, __LINE__,
+ lvif, vap, vap->iv_bss, lvif->lvif_bss,
+ (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
+ lvif->lvif_bss_synched, ni, lsta);
+
+ /*
+ * Reference the "ni" for caching the lsta/ni in lvif->lvif_bss.
+ * Given we cache lsta we use lsta->ni instead of ni here (even though
+ * lsta->ni == ni) to be distinct from the rest of the code where we do
+ * assume that ni == vap->iv_bss which it may or may not be.
+ * So do NOT use iv_bss here anymore as that may have diverged from our
+ * function local ni already while ic was unlocked and would lead to
+ * inconsistencies. Go and see if we lost a race and do not update
+ * lvif_bss_synched in that case.
+ */
+ ieee80211_ref_node(lsta->ni);
+ lvif->lvif_bss = lsta;
+ if (lsta->ni == vap->iv_bss) {
+ lvif->lvif_bss_synched = synched = true;
+ } else {
+ /* Set to un-synched no matter what. */
+ lvif->lvif_bss_synched = synched = false;
+ /*
+ * We do not error as someone has to take us down.
+ * If we are followed by a 2nd, new net80211::join1() going to
+ * AUTH lkpi_sta_a_to_a() will error, lkpi_sta_auth_to_{scan,init}()
+ * will take the lvif->lvif_bss node down eventually.
+ * What happens with the vap->iv_bss node will entirely be up
+ * to net80211 as we never used the node beyond alloc()/free()
+ * and we do not hold an extra reference for that anymore given
+ * ni : lsta == 1:1.
+ * Problem is if we do not error a MGMT/AUTH frame will be
+ * sent from net80211::sta_newstate(); disable lsta queue below.
+ */
+ }
+ LKPI_80211_LVIF_UNLOCK(lvif);
+ /*
+ * Make sure in case the sta did not change and we re-added it,
+ * that we can tx again but only if the vif/iv_bss are in sync.
+ * Otherwise this should prevent the MGMT/AUTH frame from being
+ * sent triggering a warning in iwlwifi.
+ */
+ LKPI_80211_LSTA_TXQ_LOCK(lsta);
+ lsta->txq_ready = synched;
+ LKPI_80211_LSTA_TXQ_UNLOCK(lsta);
+ goto out_relocked;
+
out:
- LKPI_80211_LHW_UNLOCK(lhw);
+ wiphy_unlock(hw->wiphy);
IEEE80211_LOCK(vap->iv_ic);
+out_relocked:
/*
- * Release the reference that keop the ni stable locally
+ * Release the reference that kept the ni stable locally
* during the work of this function.
*/
if (ni != NULL)
@@ -1390,13 +2420,13 @@ lkpi_sta_auth_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int
lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
IEEE80211_UNLOCK(vap->iv_ic);
- LKPI_80211_LHW_LOCK(lhw);
+ 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, true, true);
+ lkpi_wake_tx_queues(hw, sta, false, true);
/* flush, no drop */
lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), false);
@@ -1449,24 +2479,10 @@ lkpi_sta_auth_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int
/* conf_tx */
- /* Take the chan ctx down. */
- if (vif->chanctx_conf != NULL) {
- struct lkpi_chanctx *lchanctx;
- struct ieee80211_chanctx_conf *conf;
-
- conf = vif->chanctx_conf;
- /* Remove vif context. */
- lkpi_80211_mo_unassign_vif_chanctx(hw, vif, &vif->bss_conf, &vif->chanctx_conf);
- /* NB: vif->chanctx_conf is NULL now. */
-
- /* Remove chan ctx. */
- lkpi_80211_mo_remove_chanctx(hw, conf);
- lchanctx = CHANCTX_CONF_TO_LCHANCTX(conf);
- free(lchanctx, M_LKPI80211);
- }
+ lkpi_remove_chanctx(hw, vif);
out:
- LKPI_80211_LHW_UNLOCK(lhw);
+ wiphy_unlock(hw->wiphy);
IEEE80211_LOCK(vap->iv_ic);
return (error);
}
@@ -1499,7 +2515,7 @@ lkpi_sta_auth_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, in
vif = LVIF_TO_VIF(lvif);
IEEE80211_UNLOCK(vap->iv_ic);
- LKPI_80211_LHW_LOCK(lhw);
+ wiphy_lock(hw->wiphy);
LKPI_80211_LVIF_LOCK(lvif);
/* XXX-BZ KASSERT later? */
@@ -1552,7 +2568,7 @@ lkpi_sta_auth_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, in
}
/* Wake tx queue to get packet out. */
- lkpi_wake_tx_queues(hw, LSTA_TO_STA(lsta), true, true);
+ lkpi_wake_tx_queues(hw, LSTA_TO_STA(lsta), false, true);
/*
* <twiddle> .. we end up in "assoc_to_run"
@@ -1565,7 +2581,7 @@ lkpi_sta_auth_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, in
*/
out:
- LKPI_80211_LHW_UNLOCK(lhw);
+ wiphy_unlock(hw->wiphy);
IEEE80211_LOCK(vap->iv_ic);
return (error);
}
@@ -1588,7 +2604,7 @@ lkpi_sta_a_to_a(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
vif = LVIF_TO_VIF(lvif);
IEEE80211_UNLOCK(vap->iv_ic);
- LKPI_80211_LHW_LOCK(lhw);
+ wiphy_lock(hw->wiphy);
LKPI_80211_LVIF_LOCK(lvif);
/* XXX-BZ KASSERT later? */
@@ -1632,7 +2648,7 @@ lkpi_sta_a_to_a(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
error = 0;
out:
- LKPI_80211_LHW_UNLOCK(lhw);
+ wiphy_unlock(hw->wiphy);
IEEE80211_LOCK(vap->iv_ic);
return (error);
@@ -1658,7 +2674,7 @@ _lkpi_sta_assoc_to_down(struct ieee80211vap *vap, enum ieee80211_state nstate, i
vif = LVIF_TO_VIF(lvif);
IEEE80211_UNLOCK(vap->iv_ic);
- LKPI_80211_LHW_LOCK(lhw);
+ wiphy_lock(hw->wiphy);
LKPI_80211_LVIF_LOCK(lvif);
#ifdef LINUXKPI_DEBUG_80211
@@ -1689,14 +2705,15 @@ _lkpi_sta_assoc_to_down(struct ieee80211vap *vap, enum ieee80211_state nstate, i
!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;
}
- LKPI_80211_LHW_UNLOCK(lhw);
+ 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 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) "
@@ -1705,12 +2722,16 @@ _lkpi_sta_assoc_to_down(struct ieee80211vap *vap, enum ieee80211_state nstate, i
}
IEEE80211_UNLOCK(vap->iv_ic);
- LKPI_80211_LHW_LOCK(lhw);
+
+ /* 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, true, true);
+ lkpi_wake_tx_queues(hw, sta, false, true);
/* flush, no drop */
lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), false);
@@ -1719,6 +2740,7 @@ _lkpi_sta_assoc_to_down(struct ieee80211vap *vap, enum ieee80211_state nstate, i
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;
}
@@ -1742,16 +2764,11 @@ _lkpi_sta_assoc_to_down(struct ieee80211vap *vap, enum ieee80211_state nstate, i
goto out;
}
- lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
+ /* See comment in lkpi_sta_run_to_init(). */
+ bss_changed = 0;
+ bss_changed |= lkpi_disassoc(sta, vif, lhw);
- /* Update bss info (bss_info_changed) (assoc, aid, ..). */
- /*
- * 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.
- */
- lkpi_disassoc(sta, vif, lhw);
+ 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));
@@ -1768,7 +2785,6 @@ _lkpi_sta_assoc_to_down(struct ieee80211vap *vap, enum ieee80211_state nstate, i
lkpi_lsta_dump(lsta, ni, __func__, __LINE__); /* sta no longer save to use. */
IMPROVE("Any bss_info changes to announce?");
- bss_changed = 0;
vif->bss_conf.qos = 0;
bss_changed |= BSS_CHANGED_QOS;
vif->cfg.ssid_len = 0;
@@ -1791,25 +2807,11 @@ _lkpi_sta_assoc_to_down(struct ieee80211vap *vap, enum ieee80211_state nstate, i
/* conf_tx */
- /* Take the chan ctx down. */
- if (vif->chanctx_conf != NULL) {
- struct lkpi_chanctx *lchanctx;
- struct ieee80211_chanctx_conf *conf;
-
- conf = vif->chanctx_conf;
- /* Remove vif context. */
- lkpi_80211_mo_unassign_vif_chanctx(hw, vif, &vif->bss_conf, &vif->chanctx_conf);
- /* NB: vif->chanctx_conf is NULL now. */
-
- /* Remove chan ctx. */
- lkpi_80211_mo_remove_chanctx(hw, conf);
- lchanctx = CHANCTX_CONF_TO_LCHANCTX(conf);
- free(lchanctx, M_LKPI80211);
- }
+ lkpi_remove_chanctx(hw, vif);
error = EALREADY;
out:
- LKPI_80211_LHW_UNLOCK(lhw);
+ wiphy_unlock(hw->wiphy);
IEEE80211_LOCK(vap->iv_ic);
outni:
return (error);
@@ -1868,7 +2870,7 @@ lkpi_sta_assoc_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int
vif = LVIF_TO_VIF(lvif);
IEEE80211_UNLOCK(vap->iv_ic);
- LKPI_80211_LHW_LOCK(lhw);
+ wiphy_lock(hw->wiphy);
LKPI_80211_LVIF_LOCK(lvif);
/* XXX-BZ KASSERT later? */
@@ -1944,7 +2946,6 @@ 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);
/* - change_chanctx (if needed)
@@ -1977,10 +2978,14 @@ lkpi_sta_assoc_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int
IMPROVE("net80211 does not consider node authorized");
}
-#if defined(LKPI_80211_HT)
IMPROVE("Is this the right spot, has net80211 done all updates already?");
- lkpi_sta_sync_ht_from_ni(sta, ni, NULL);
-#endif
+ lkpi_sta_sync_from_ni(hw, vif, sta, ni, true);
+
+ /* Update thresholds. */
+ hw->wiphy->frag_threshold = vap->iv_fragthreshold;
+ lkpi_80211_mo_set_frag_threshold(hw, vap->iv_fragthreshold);
+ hw->wiphy->rts_threshold = vap->iv_rtsthreshold;
+ lkpi_80211_mo_set_rts_threshold(hw, vap->iv_rtsthreshold);
/* Update sta_state (ASSOC to AUTHORIZED). */
KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
@@ -2007,7 +3012,7 @@ lkpi_sta_assoc_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int
lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
out:
- LKPI_80211_LHW_UNLOCK(lhw);
+ wiphy_unlock(hw->wiphy);
IEEE80211_LOCK(vap->iv_ic);
return (error);
}
@@ -2066,7 +3071,7 @@ 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);
- LKPI_80211_LHW_LOCK(lhw);
+ wiphy_lock(hw->wiphy);
/* flush, drop. */
lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), true);
@@ -2076,11 +3081,12 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int
!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;
}
- LKPI_80211_LHW_UNLOCK(lhw);
+ wiphy_unlock(hw->wiphy);
IEEE80211_LOCK(vap->iv_ic);
/* Call iv_newstate first so we get potential DISASSOC packet out. */
@@ -2092,12 +3098,16 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int
}
IEEE80211_UNLOCK(vap->iv_ic);
- LKPI_80211_LHW_LOCK(lhw);
+
+ /* 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, true, true);
+ lkpi_wake_tx_queues(hw, sta, false, true);
/* flush, no drop */
lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), false);
@@ -2106,6 +3116,7 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int
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;
}
@@ -2133,6 +3144,22 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int
lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
+#ifdef LKPI_80211_HW_CRYPTO
+ if (lkpi_hwcrypto) {
+ error = lkpi_sta_del_keys(hw, vif, lsta);
+ if (error != 0) {
+ ic_printf(vap->iv_ic, "%s:%d: lkpi_sta_del_keys "
+ "failed: %d\n", __func__, __LINE__, error);
+ /*
+ * Either drv/fw will crash or cleanup itself,
+ * otherwise net80211 will delete the keys (at a
+ * less appropriate time).
+ */
+ /* goto out; */
+ }
+ }
+#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 "
@@ -2153,7 +3180,7 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int
error = EALREADY;
out:
- LKPI_80211_LHW_UNLOCK(lhw);
+ wiphy_unlock(hw->wiphy);
IEEE80211_LOCK(vap->iv_ic);
outni:
return (error);
@@ -2179,7 +3206,7 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
vif = LVIF_TO_VIF(lvif);
IEEE80211_UNLOCK(vap->iv_ic);
- LKPI_80211_LHW_LOCK(lhw);
+ wiphy_lock(hw->wiphy);
LKPI_80211_LVIF_LOCK(lvif);
#ifdef LINUXKPI_DEBUG_80211
@@ -2210,11 +3237,12 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
!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;
}
- LKPI_80211_LHW_UNLOCK(lhw);
+ wiphy_unlock(hw->wiphy);
IEEE80211_LOCK(vap->iv_ic);
/* Call iv_newstate first so we get potential DISASSOC packet out. */
@@ -2226,12 +3254,16 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
}
IEEE80211_UNLOCK(vap->iv_ic);
- LKPI_80211_LHW_LOCK(lhw);
+
+ /* 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, true, true);
+ lkpi_wake_tx_queues(hw, sta, false, true);
/* flush, no drop */
lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), false);
@@ -2240,6 +3272,7 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
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;
}
@@ -2265,6 +3298,22 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
+#ifdef LKPI_80211_HW_CRYPTO
+ if (lkpi_hwcrypto) {
+ error = lkpi_sta_del_keys(hw, vif, lsta);
+ if (error != 0) {
+ ic_printf(vap->iv_ic, "%s:%d: lkpi_sta_del_keys "
+ "failed: %d\n", __func__, __LINE__, error);
+ /*
+ * Either drv/fw will crash or cleanup itself,
+ * otherwise net80211 will delete the keys (at a
+ * less appropriate time).
+ */
+ /* goto out; */
+ }
+ }
+#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 "
@@ -2289,14 +3338,33 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
goto out;
}
- lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
-
- /* Update bss info (bss_info_changed) (assoc, aid, ..). */
+ bss_changed = 0;
/*
+ * 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.
+ * See comment there; removes the sta from fw if not careful
+ * (bss_info_changed() change is executed right away).
+ *
+ * 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.
+ *
+ * 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()
+ * 4) call unassign_vif_chanctx
+ * 5) call lkpi_hw_conf_idle
+ * 6) call remove_chanctx
*/
- lkpi_disassoc(sta, vif, lhw);
+ bss_changed |= lkpi_disassoc(sta, vif, lhw);
+
+ 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));
@@ -2310,15 +3378,19 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
goto out;
}
+ 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?");
- bss_changed = 0;
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_???? */
lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
LKPI_80211_LVIF_LOCK(lvif);
@@ -2326,7 +3398,6 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
lvif->lvif_bss = NULL;
lvif->lvif_bss_synched = false;
LKPI_80211_LVIF_UNLOCK(lvif);
- lkpi_lsta_remove(lsta, lvif);
/*
* 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
@@ -2336,25 +3407,11 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
/* conf_tx */
- /* Take the chan ctx down. */
- if (vif->chanctx_conf != NULL) {
- struct lkpi_chanctx *lchanctx;
- struct ieee80211_chanctx_conf *conf;
-
- conf = vif->chanctx_conf;
- /* Remove vif context. */
- lkpi_80211_mo_unassign_vif_chanctx(hw, vif, &vif->bss_conf, &vif->chanctx_conf);
- /* NB: vif->chanctx_conf is NULL now. */
-
- /* Remove chan ctx. */
- lkpi_80211_mo_remove_chanctx(hw, conf);
- lchanctx = CHANCTX_CONF_TO_LCHANCTX(conf);
- free(lchanctx, M_LKPI80211);
- }
+ lkpi_remove_chanctx(hw, vif);
error = EALREADY;
out:
- LKPI_80211_LHW_UNLOCK(lhw);
+ wiphy_unlock(hw->wiphy);
IEEE80211_LOCK(vap->iv_ic);
outni:
return (error);
@@ -2558,6 +3615,9 @@ lkpi_wme_update(struct lkpi_hw *lhw, struct ieee80211vap *vap, bool planned)
int error;
uint16_t ac;
+ hw = LHW_TO_HW(lhw);
+ lockdep_assert_wiphy(hw->wiphy);
+
IMPROVE();
KASSERT(WME_NUM_AC == IEEE80211_NUM_ACS, ("%s: WME_NUM_AC %d != "
"IEEE80211_NUM_ACS %d\n", __func__, WME_NUM_AC, IEEE80211_NUM_ACS));
@@ -2584,12 +3644,10 @@ lkpi_wme_update(struct lkpi_hw *lhw, struct ieee80211vap *vap, bool planned)
wmeparr[ac] = chp.cap_wmeParams[ac];
IEEE80211_UNLOCK(ic);
- hw = LHW_TO_HW(lhw);
lvif = VAP_TO_LVIF(vap);
vif = LVIF_TO_VIF(lvif);
/* Configure tx queues (conf_tx) & send BSS_CHANGED_QOS. */
- LKPI_80211_LHW_LOCK(lhw);
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
struct wmeParams *wmep;
@@ -2604,7 +3662,6 @@ 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);
}
- LKPI_80211_LHW_UNLOCK(lhw);
changed = BSS_CHANGED_QOS;
if (!planned)
lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, changed);
@@ -2619,6 +3676,7 @@ lkpi_ic_wme_update(struct ieee80211com *ic)
#ifdef LKPI_80211_WME
struct ieee80211vap *vap;
struct lkpi_hw *lhw;
+ struct ieee80211_hw *hw;
IMPROVE("Use the per-VAP callback in net80211.");
vap = TAILQ_FIRST(&ic->ic_vaps);
@@ -2626,12 +3684,46 @@ lkpi_ic_wme_update(struct ieee80211com *ic)
return (0);
lhw = ic->ic_softc;
+ hw = LHW_TO_HW(lhw);
+ wiphy_lock(hw->wiphy);
lkpi_wme_update(lhw, vap, false);
+ wiphy_unlock(hw->wiphy);
#endif
return (0); /* unused */
}
+/*
+ * Change link-layer address on the vif (if the vap is not started/"UP").
+ * This can happen if a user changes 'ether' using ifconfig.
+ * The code is based on net80211/ieee80211_freebsd.c::wlan_iflladdr() but
+ * we do use a per-[l]vif event handler to be sure we exist as we
+ * cannot assume that from every vap derives a vif and we have a hard
+ * time checking based on net80211 information.
+ * Should this ever become a real problem we could add a callback function
+ * to wlan_iflladdr() to be set optionally but that would be for a
+ * single-consumer (or needs a list) -- was just too complicated for an
+ * otherwise perfect mechanism FreeBSD already provides.
+ */
+static void
+lkpi_vif_iflladdr(void *arg, struct ifnet *ifp)
+{
+ struct epoch_tracker et;
+ struct ieee80211_vif *vif;
+
+ NET_EPOCH_ENTER(et);
+ /* NB: identify vap's by if_transmit; left as an extra check. */
+ if (if_gettransmitfn(ifp) != ieee80211_vap_transmit ||
+ (if_getflags(ifp) & IFF_UP) != 0) {
+ NET_EPOCH_EXIT(et);
+ return;
+ }
+
+ vif = arg;
+ IEEE80211_ADDR_COPY(vif->bss_conf.addr, if_getlladdr(ifp));
+ NET_EPOCH_EXIT(et);
+}
+
static struct ieee80211vap *
lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
int unit, enum ieee80211_opmode opmode, int flags,
@@ -2645,6 +3737,7 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
struct ieee80211_vif *vif;
struct ieee80211_tx_queue_params txqp;
enum ieee80211_bss_changed changed;
+ struct sysctl_oid *node;
size_t len;
int error, i;
uint16_t ac;
@@ -2660,8 +3753,9 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
lvif = malloc(len, M_80211_VAP, M_WAITOK | M_ZERO);
mtx_init(&lvif->mtx, "lvif", NULL, MTX_DEF);
- TAILQ_INIT(&lvif->lsta_head);
+ INIT_LIST_HEAD(&lvif->lsta_list);
lvif->lvif_bss = NULL;
+ refcount_init(&lvif->nt_unlocked, 0);
lvif->lvif_bss_synched = false;
vap = LVIF_TO_VAP(lvif);
@@ -2676,12 +3770,14 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
/* XXX-BZ hardcoded for now! */
#if 1
- vif->chanctx_conf = NULL;
+ RCU_INIT_POINTER(vif->bss_conf.chanctx_conf, NULL);
vif->bss_conf.vif = vif;
/* vap->iv_myaddr is not set until net80211::vap_setup or vap_attach. */
IEEE80211_ADDR_COPY(vif->bss_conf.addr, mac);
+ 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.chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
+ 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;
@@ -2757,7 +3853,7 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
/* 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");
- LKPI_80211_LHW_LOCK(lhw);
+ wiphy_lock(hw->wiphy);
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
bzero(&txqp, sizeof(txqp));
@@ -2770,7 +3866,7 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
ic_printf(ic, "%s: conf_tx ac %u failed %d\n",
__func__, ac, error);
}
- LKPI_80211_LHW_UNLOCK(lhw);
+ wiphy_unlock(hw->wiphy);
changed = BSS_CHANGED_QOS;
lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, changed);
@@ -2787,13 +3883,15 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
lvif->iv_update_bss = vap->iv_update_bss;
vap->iv_update_bss = lkpi_iv_update_bss;
- /* Key management. */
- if (lhw->ops->set_key != NULL) {
#ifdef LKPI_80211_HW_CRYPTO
+ /* Key management. */
+ if (lkpi_hwcrypto && lhw->ops->set_key != NULL) {
vap->iv_key_set = lkpi_iv_key_set;
vap->iv_key_delete = lkpi_iv_key_delete;
-#endif
+ vap->iv_key_update_begin = lkpi_iv_key_update_begin;
+ vap->iv_key_update_end = lkpi_iv_key_update_end;
}
+#endif
#ifdef LKPI_80211_HT
/* Stay with the iv_ampdu_rxmax,limit / iv_ampdu_density defaults until later. */
@@ -2805,6 +3903,19 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
ieee80211_vap_attach(vap, ieee80211_media_change,
ieee80211_media_status, mac);
+#ifdef LKPI_80211_HT
+ /*
+ * Modern chipset/fw/drv will do A-MPDU in drv/fw and fail
+ * to do so if they cannot do the crypto too.
+ */
+ if (!lkpi_hwcrypto && ieee80211_hw_check(hw, AMPDU_AGGREGATION))
+ vap->iv_flags_ht &= ~IEEE80211_FHT_AMPDU_RX;
+#endif
+#if defined(LKPI_80211_HT)
+ /* 20250125-BZ Keep A-MPDU TX cleared until we sorted out AddBA for all drivers. */
+ vap->iv_flags_ht &= ~IEEE80211_FHT_AMPDU_TX;
+#endif
+
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;
@@ -2816,6 +3927,20 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
hw->wiphy->rts_threshold = vap->iv_rtsthreshold;
lkpi_80211_mo_set_rts_threshold(hw, vap->iv_rtsthreshold);
/* any others? */
+
+ /* Add per-VIF/VAP sysctls. */
+ sysctl_ctx_init(&lvif->sysctl_ctx);
+
+ node = SYSCTL_ADD_NODE(&lvif->sysctl_ctx,
+ SYSCTL_CHILDREN(&sysctl___compat_linuxkpi_80211),
+ OID_AUTO, if_name(vap->iv_ifp),
+ CTLFLAG_RD | CTLFLAG_SKIP | CTLFLAG_MPSAFE, NULL, "VIF Information");
+
+ SYSCTL_ADD_PROC(&lvif->sysctl_ctx,
+ SYSCTL_CHILDREN(node), OID_AUTO, "dump_stas",
+ CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, lvif, 0,
+ lkpi_80211_dump_stas, "A", "Dump sta statistics of this vif");
+
IMPROVE();
return (vap);
@@ -2853,6 +3978,11 @@ lkpi_ic_vap_delete(struct ieee80211vap *vap)
lhw = ic->ic_softc;
hw = LHW_TO_HW(lhw);
+ EVENTHANDLER_DEREGISTER(iflladdr_event, lvif->lvif_ifllevent);
+
+ /* Clear up per-VIF/VAP sysctls. */
+ sysctl_ctx_free(&lvif->sysctl_ctx);
+
LKPI_80211_LHW_LVIF_LOCK(lhw);
TAILQ_REMOVE(&lhw->lvif_head, lvif, lvif_entry);
LKPI_80211_LHW_LVIF_UNLOCK(lhw);
@@ -2865,7 +3995,7 @@ lkpi_ic_vap_delete(struct ieee80211vap *vap)
lkpi_80211_mo_remove_interface(hw, vif);
/* Single VAP, so we can do this here. */
- lkpi_80211_mo_stop(hw);
+ lkpi_80211_mo_stop(hw, false); /* XXX SUSPEND */
mtx_destroy(&lvif->mtx);
free(lvif, M_80211_VAP);
@@ -2898,8 +4028,8 @@ static void
lkpi_ic_parent(struct ieee80211com *ic)
{
struct lkpi_hw *lhw;
-#ifdef HW_START_STOP
struct ieee80211_hw *hw;
+#ifdef HW_START_STOP
int error;
#endif
bool start_all;
@@ -2907,13 +4037,11 @@ lkpi_ic_parent(struct ieee80211com *ic)
IMPROVE();
lhw = ic->ic_softc;
-#ifdef HW_START_STOP
hw = LHW_TO_HW(lhw);
-#endif
start_all = false;
/* IEEE80211_UNLOCK(ic); */
- LKPI_80211_LHW_LOCK(lhw);
+ wiphy_lock(hw->wiphy);
if (ic->ic_nrunning > 0) {
#ifdef HW_START_STOP
error = lkpi_80211_mo_start(hw);
@@ -2922,10 +4050,10 @@ lkpi_ic_parent(struct ieee80211com *ic)
start_all = true;
} else {
#ifdef HW_START_STOP
- lkpi_80211_mo_stop(hw);
+ lkpi_80211_mo_stop(hw, false); /* XXX SUSPEND */
#endif
}
- LKPI_80211_LHW_UNLOCK(lhw);
+ wiphy_unlock(hw->wiphy);
/* IEEE80211_LOCK(ic); */
if (start_all)
@@ -2996,12 +4124,26 @@ lkpi_scan_ies_add(uint8_t *p, struct ieee80211_scan_ies *scan_ies,
channels = supband->channels;
chan = NULL;
for (i = 0; i < supband->n_channels; i++) {
+ uint32_t flags;
if (channels[i].flags & IEEE80211_CHAN_DISABLED)
continue;
+ flags = 0;
+ switch (band) {
+ case NL80211_BAND_2GHZ:
+ flags |= IEEE80211_CHAN_G;
+ break;
+ case NL80211_BAND_5GHZ:
+ flags |= IEEE80211_CHAN_A;
+ break;
+ default:
+ panic("%s:%d: unupported band %d\n",
+ __func__, __LINE__, band);
+ }
+
chan = ieee80211_find_channel(ic,
- channels[i].center_freq, 0);
+ channels[i].center_freq, flags);
if (chan != NULL)
break;
}
@@ -3025,7 +4167,8 @@ lkpi_scan_ies_add(uint8_t *p, struct ieee80211_scan_ies *scan_ies,
}
#endif
#if defined(LKPI_80211_VHT)
- if ((vap->iv_vht_flags & IEEE80211_FVHT_VHT) != 0) {
+ if (band == NL80211_BAND_5GHZ &&
+ (vap->iv_vht_flags & IEEE80211_FVHT_VHT) != 0) {
struct ieee80211_channel *c;
c = ieee80211_ht_adjust_channel(ic, ic->ic_curchan,
@@ -3106,7 +4249,6 @@ sw_scan:
/* XXX want to adjust ss end time/ maxdwell? */
} else {
- struct ieee80211_channel *c;
struct ieee80211_scan_request *hw_req;
struct linuxkpi_ieee80211_channel *lc, **cpp;
struct cfg80211_ssid *ssids;
@@ -3123,14 +4265,31 @@ sw_scan:
band_mask = 0;
nchan = 0;
- for (i = ss->ss_next; i < ss->ss_last; i++) {
- nchan++;
- band = lkpi_net80211_chan_to_nl80211_band(
- ss->ss_chans[ss->ss_next + i]);
- band_mask |= (1 << band);
- }
-
- if (!ieee80211_hw_check(hw, SINGLE_SCAN_ON_ALL_BANDS)) {
+ if (ieee80211_hw_check(hw, SINGLE_SCAN_ON_ALL_BANDS)) {
+#if 0 /* Avoid net80211 scan lists until it has proper scan offload support. */
+ for (i = ss->ss_next; i < ss->ss_last; i++) {
+ nchan++;
+ band = lkpi_net80211_chan_to_nl80211_band(
+ ss->ss_chans[ss->ss_next + i]);
+ band_mask |= (1 << band);
+ }
+#else
+ /* Instead we scan for all channels all the time. */
+ for (band = 0; band < NUM_NL80211_BANDS; band++) {
+ switch (band) {
+ case NL80211_BAND_2GHZ:
+ case NL80211_BAND_5GHZ:
+ break;
+ default:
+ continue;
+ }
+ if (hw->wiphy->bands[band] != NULL) {
+ nchan += hw->wiphy->bands[band]->n_channels;
+ band_mask |= (1 << band);
+ }
+ }
+#endif
+ } else {
IMPROVE("individual band scans not yet supported, only scanning first band");
/* In theory net80211 should drive this. */
/* Probably we need to add local logic for now;
@@ -3184,9 +4343,11 @@ sw_scan:
*(cpp + i) =
(struct linuxkpi_ieee80211_channel *)(lc + i);
}
+#if 0 /* Avoid net80211 scan lists until it has proper scan offload support. */
for (i = 0; i < nchan; i++) {
- c = ss->ss_chans[ss->ss_next + i];
+ struct ieee80211_channel *c;
+ c = ss->ss_chans[ss->ss_next + i];
lc->hw_value = c->ic_ieee;
lc->center_freq = c->ic_freq; /* XXX */
/* lc->flags */
@@ -3195,6 +4356,27 @@ sw_scan:
/* lc-> ... */
lc++;
}
+#else
+ for (band = 0; band < NUM_NL80211_BANDS; band++) {
+ struct ieee80211_supported_band *supband;
+ struct linuxkpi_ieee80211_channel *channels;
+
+ /* Band disabled for scanning? */
+ if ((band_mask & (1 << band)) == 0)
+ continue;
+
+ /* Nothing to scan in band? */
+ supband = hw->wiphy->bands[band];
+ if (supband == NULL || supband->n_channels == 0)
+ continue;
+
+ channels = supband->channels;
+ for (i = 0; i < supband->n_channels; i++) {
+ *lc = channels[i];
+ lc++;
+ }
+ }
+#endif
hw_req->req.n_ssids = ssid_count;
if (hw_req->req.n_ssids > 0) {
@@ -3411,7 +4593,7 @@ lkpi_ic_set_channel(struct ieee80211com *ic)
hw = LHW_TO_HW(lhw);
cfg80211_chandef_create(&hw->conf.chandef, chan,
#ifdef LKPI_80211_HT
- (ic->ic_htcaps & IEEE80211_HTC_HT) ? 0 :
+ (ic->ic_flags_ht & IEEE80211_FHT_HT) ? NL80211_CHAN_HT20 :
#endif
NL80211_CHAN_NO_HT);
@@ -3535,26 +4717,55 @@ lkpi_ic_node_free(struct ieee80211_node *ni)
lhw->ic_node_free(ni);
}
+/*
+ * lkpi_xmit() called from both the (*ic_raw_xmit) as well as the (*ic_transmit)
+ * call path.
+ * Unfortunately they have slightly different invariants. See
+ * ieee80211_raw_output() and ieee80211_parent_xmitpkt().
+ * Both take care of the ni reference in case of error, and otherwise during
+ * the callback after transmit.
+ * The difference is that in case of error (*ic_raw_xmit) needs us to release
+ * the mbuf, while (*ic_transmit) will free the mbuf itself.
+ */
static int
-lkpi_ic_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
- const struct ieee80211_bpf_params *params __unused)
+lkpi_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params __unused,
+ bool freem)
{
struct lkpi_sta *lsta;
+ int error;
lsta = ni->ni_drv_data;
LKPI_80211_LSTA_TXQ_LOCK(lsta);
+#if 0
+ if (!lsta->added_to_drv || !lsta->txq_ready) {
+#else
+ /*
+ * Backout this part of 886653492945f which breaks rtw88 or
+ * in general drivers without (*sta_state)() but only the
+ * legacy fallback to (*sta_add)().
+ */
if (!lsta->txq_ready) {
+#endif
LKPI_80211_LSTA_TXQ_UNLOCK(lsta);
- /*
- * Free the mbuf (do NOT release ni ref for the m_pkthdr.rcvif!
- * ieee80211_raw_output() does that in case of error).
- */
- m_free(m);
+ if (freem)
+ m_free(m);
return (ENETDOWN);
}
/* Queue the packet and enqueue the task to handle it. */
- mbufq_enqueue(&lsta->txq, m);
+ error = mbufq_enqueue(&lsta->txq, m);
+ if (error != 0) {
+ LKPI_80211_LSTA_TXQ_UNLOCK(lsta);
+ if (freem)
+ m_free(m);
+#ifdef LINUXKPI_DEBUG_80211
+ if (linuxkpi_debug_80211 & D80211_TRACE_TX)
+ ic_printf(ni->ni_ic, "%s: mbufq_enqueue failed: %d\n",
+ __func__, error);
+#endif
+ return (ENETDOWN);
+ }
taskqueue_enqueue(taskqueue_thread, &lsta->txq_task);
LKPI_80211_LSTA_TXQ_UNLOCK(lsta);
@@ -3568,13 +4779,193 @@ lkpi_ic_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
return (0);
}
+static int
+lkpi_ic_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params __unused)
+{
+ return (lkpi_xmit(ni, m, NULL, true));
+}
+
+#ifdef LKPI_80211_HW_CRYPTO
+/*
+ * This is a bit of a hack given we know we are operating on a
+ * single frame and we know that hardware will deal with it.
+ * But otherwise the enmic bit and the encrypt bit need to be
+ * decoupled.
+ */
+static int
+lkpi_hw_crypto_prepare_tkip(struct ieee80211_key *k,
+ struct ieee80211_key_conf *kc, struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr;
+ uint32_t hlen, hdrlen;
+ uint8_t *p;
+
+ /*
+ * TKIP only happens on data.
+ */
+ hdr = (void *)skb->data;
+ if (!ieee80211_is_data_present(hdr->frame_control))
+ return (0);
+
+ /*
+ * "enmic" (though we do not do that).
+ */
+ /* any conditions to not apply this? */
+ if (skb_tailroom(skb) < k->wk_cipher->ic_miclen)
+ return (ENOBUFS);
+
+ p = skb_put(skb, k->wk_cipher->ic_miclen);
+ if ((kc->flags & IEEE80211_KEY_FLAG_PUT_MIC_SPACE) != 0)
+ goto encrypt;
+
+ /*
+ * (*enmic) which we hopefully do not have to do with hw accel.
+ * That means if we make it here we have a problem.
+ */
+ TODO("(*enmic)");
+ return (ENXIO);
+
+encrypt:
+ /*
+ * "encrypt" (though we do not do that).
+ */
+ /*
+ * Check if we have anything to do as requested by driver
+ * or if we are done?
+ */
+ if ((kc->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE) == 0 &&
+ (kc->flags & IEEE80211_KEY_FLAG_GENERATE_IV) == 0)
+ return (0);
+
+ hlen = k->wk_cipher->ic_header;
+ if (skb_headroom(skb) < hlen)
+ return (ENOBUFS);
+
+ hdr = (void *)skb->data;
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
+ p = skb_push(skb, hlen);
+ memmove(p, p + hlen, hdrlen);
+
+ /* If driver request space only we are done. */
+ if ((kc->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE) != 0)
+ return (0);
+
+ p += hdrlen;
+ k->wk_cipher->ic_setiv(k, p);
+
+ /* If we make it hear we do sw encryption. */
+ TODO("sw encrypt");
+ return (ENXIO);
+}
+
+static int
+lkpi_hw_crypto_prepare_ccmp(struct ieee80211_key *k,
+ struct ieee80211_key_conf *kc, struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr;
+ uint32_t hlen, hdrlen;
+ uint8_t *p;
+
+ hdr = (void *)skb->data;
+
+ /*
+ * Check if we have anythig to do as requested by driver
+ * or if we are done?
+ */
+ if ((kc->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE) == 0 &&
+ (kc->flags & IEEE80211_KEY_FLAG_GENERATE_IV) == 0 &&
+ /* MFP */
+ !((kc->flags & IEEE80211_KEY_FLAG_GENERATE_IV_MGMT) != 0 &&
+ ieee80211_is_mgmt(hdr->frame_control)))
+ return (0);
+
+ hlen = k->wk_cipher->ic_header;
+ if (skb_headroom(skb) < hlen)
+ return (ENOBUFS);
+
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
+ p = skb_push(skb, hlen);
+ memmove(p, p + hlen, hdrlen);
+
+ /* If driver request space only we are done. */
+ if ((kc->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE) != 0)
+ return (0);
+
+ p += hdrlen;
+ k->wk_cipher->ic_setiv(k, p);
+
+ return (0);
+}
+
+static int
+lkpi_hw_crypto_prepare(struct lkpi_sta *lsta, struct ieee80211_key *k,
+ struct sk_buff *skb)
+{
+ struct ieee80211_tx_info *info;
+ struct ieee80211_key_conf *kc;
+
+ KASSERT(lsta != NULL, ("%s: lsta is NULL", __func__));
+ KASSERT(k != NULL, ("%s: key is NULL", __func__));
+ KASSERT(skb != NULL, ("%s: skb is NULL", __func__));
+
+ kc = lsta->kc[k->wk_keyix];
+
+ info = IEEE80211_SKB_CB(skb);
+ info->control.hw_key = kc;
+
+ /* MUST NOT happen. KASSERT? */
+ if (kc == NULL) {
+ ic_printf(lsta->ni->ni_ic, "%s: lsta %p k %p skb %p, "
+ "kc is NULL on hw crypto offload\n", __func__, lsta, k, skb);
+ return (ENXIO);
+ }
+
+ switch (kc->cipher) {
+ case WLAN_CIPHER_SUITE_TKIP:
+ return (lkpi_hw_crypto_prepare_tkip(k, kc, skb));
+ case WLAN_CIPHER_SUITE_CCMP:
+ return (lkpi_hw_crypto_prepare_ccmp(k, kc, skb));
+ case WLAN_CIPHER_SUITE_GCMP:
+ return (lkpi_hw_crypto_prepare_ccmp(k, kc, skb));
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ case WLAN_CIPHER_SUITE_CCMP_256:
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+ default:
+ ic_printf(lsta->ni->ni_ic, "%s: lsta %p k %p kc %p skb %p, "
+ "unsupported cipher suite %u (%s)\n", __func__, lsta, k, kc,
+ skb, kc->cipher, lkpi_cipher_suite_to_name(kc->cipher));
+ return (EOPNOTSUPP);
+ }
+}
+
+static uint8_t
+lkpi_hw_crypto_tailroom(struct lkpi_sta *lsta, struct ieee80211_key *k)
+{
+ struct ieee80211_key_conf *kc;
+
+ kc = lsta->kc[k->wk_keyix];
+ if (kc == NULL)
+ return (0);
+
+ IMPROVE("which other flags need tailroom?");
+ if (kc->flags & (IEEE80211_KEY_FLAG_PUT_MIC_SPACE))
+ return (32); /* Large enough to hold everything and pow2. */
+
+ return (0);
+}
+#endif
+
static void
lkpi_80211_txq_tx_one(struct lkpi_sta *lsta, struct mbuf *m)
{
struct ieee80211_node *ni;
-#ifndef LKPI_80211_HW_CRYPTO
struct ieee80211_frame *wh;
-#endif
struct ieee80211_key *k;
struct sk_buff *skb;
struct ieee80211com *ic;
@@ -3589,7 +4980,8 @@ lkpi_80211_txq_tx_one(struct lkpi_sta *lsta, struct mbuf *m)
struct ieee80211_hdr *hdr;
struct lkpi_txq *ltxq;
void *buf;
- uint8_t ac, tid;
+ ieee80211_keyix keyix;
+ uint8_t ac, tid, tailroom;
M_ASSERTPKTHDR(m);
#ifdef LINUXKPI_DEBUG_80211
@@ -3599,20 +4991,30 @@ lkpi_80211_txq_tx_one(struct lkpi_sta *lsta, struct mbuf *m)
ni = lsta->ni;
k = NULL;
-#ifndef LKPI_80211_HW_CRYPTO
- /* Encrypt the frame if need be; XXX-BZ info->control.hw_key. */
+ keyix = IEEE80211_KEYIX_NONE;
wh = mtod(m, struct ieee80211_frame *);
if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
- /* Retrieve key for TX && do software encryption. */
- k = ieee80211_crypto_encap(ni, m);
- if (k == NULL) {
- ieee80211_free_node(ni);
- m_freem(m);
- return;
+
+#ifdef LKPI_80211_HW_CRYPTO
+ if (lkpi_hwcrypto) {
+ k = ieee80211_crypto_get_txkey(ni, m);
+ if (k != NULL && lsta->kc[k->wk_keyix] != NULL)
+ keyix = k->wk_keyix;
}
- }
#endif
+ /* Encrypt the frame if need be. */
+ if (keyix == IEEE80211_KEYIX_NONE) {
+ /* Retrieve key for TX && do software encryption. */
+ k = ieee80211_crypto_encap(ni, m);
+ if (k == NULL) {
+ ieee80211_free_node(ni);
+ m_freem(m);
+ return;
+ }
+ }
+ }
+
ic = ni->ni_ic;
lhw = ic->ic_softc;
hw = LHW_TO_HW(lhw);
@@ -3637,15 +5039,37 @@ lkpi_80211_txq_tx_one(struct lkpi_sta *lsta, struct mbuf *m)
ieee80211_radiotap_tx(ni->ni_vap, m);
}
+#ifdef LKPI_80211_HW_CRYPTO
+ if (lkpi_hwcrypto && keyix != IEEE80211_KEYIX_NONE)
+ tailroom = lkpi_hw_crypto_tailroom(lsta, k);
+ else
+#endif
+ tailroom = 0;
+
/*
* net80211 should handle hw->extra_tx_headroom.
* Though for as long as we are copying we don't mind.
* XXX-BZ rtw88 asks for too much headroom for ipv6+tcp:
* https://lists.freebsd.org/archives/freebsd-transport/2022-February/000012.html
*/
- skb = dev_alloc_skb(hw->extra_tx_headroom + m->m_pkthdr.len);
+ skb = dev_alloc_skb(hw->extra_tx_headroom + tailroom + m->m_pkthdr.len);
if (skb == NULL) {
- ic_printf(ic, "ERROR %s: skb alloc failed\n", __func__);
+ static uint8_t skb_alloc_failures = 0;
+
+ if (skb_alloc_failures++ == 0) {
+ int tid;
+
+ sta = LSTA_TO_STA(lsta);
+ ic_printf(ic, "ERROR %s: skb alloc failed %d + %d, lsta %p sta %p ni %p\n",
+ __func__, hw->extra_tx_headroom, m->m_pkthdr.len, lsta, sta, ni);
+ for (tid = 0; tid < nitems(sta->txq); tid++) {
+ if (sta->txq[tid] == NULL)
+ continue;
+ ltxq = TXQ_TO_LTXQ(sta->txq[tid]);
+ ic_printf(ic, " tid %d ltxq %p seen_dequeue %d stopped %d skb_queue_len %u\n",
+ tid, ltxq, ltxq->seen_dequeue, ltxq->stopped, skb_queue_len(&ltxq->skbq));
+ }
+ }
ieee80211_free_node(ni);
m_freem(m);
return;
@@ -3708,7 +5132,19 @@ lkpi_80211_txq_tx_one(struct lkpi_sta *lsta, struct mbuf *m)
sta = LSTA_TO_STA(lsta);
#ifdef LKPI_80211_HW_CRYPTO
- info->control.hw_key = lsta->kc;
+ if (lkpi_hwcrypto && keyix != IEEE80211_KEYIX_NONE) {
+ int error;
+
+ error = lkpi_hw_crypto_prepare(lsta, k, skb);
+ if (error != 0) {
+ /*
+ * We only have to free the skb which will free the
+ * mbuf and release the reference on the ni.
+ */
+ dev_kfree_skb(skb);
+ return;
+ }
+ }
#endif
IMPROVE();
@@ -3733,17 +5169,19 @@ lkpi_80211_txq_tx_one(struct lkpi_sta *lsta, struct mbuf *m)
skb_queue_tail(&ltxq->skbq, skb);
#ifdef LINUXKPI_DEBUG_80211
if (linuxkpi_debug_80211 & D80211_TRACE_TX)
- printf("%s:%d mo_wake_tx_queue :: %d %u lsta %p sta %p "
+ printf("%s:%d mo_wake_tx_queue :: %d %lu lsta %p sta %p "
"ni %p %6D skb %p lxtq %p { qlen %u, ac %d tid %u } "
"WAKE_TX_Q ac %d prio %u qmap %u\n",
__func__, __LINE__,
- curthread->td_tid, (unsigned int)ticks,
+ curthread->td_tid, jiffies,
lsta, sta, ni, ni->ni_macaddr, ":", skb, ltxq,
skb_queue_len(&ltxq->skbq), ltxq->txq.ac,
ltxq->txq.tid, ac, skb->priority, skb->qmap);
#endif
LKPI_80211_LTXQ_UNLOCK(ltxq);
+ wiphy_lock(hw->wiphy);
lkpi_80211_mo_wake_tx_queue(hw, &ltxq->txq);
+ wiphy_unlock(hw->wiphy);
return;
ops_tx:
@@ -3756,7 +5194,9 @@ ops_tx:
#endif
memset(&control, 0, sizeof(control));
control.sta = sta;
+ wiphy_lock(hw->wiphy);
lkpi_80211_mo_tx(hw, &control, skb);
+ wiphy_unlock(hw->wiphy);
}
static void
@@ -3765,6 +5205,7 @@ lkpi_80211_txq_task(void *ctx, int pending)
struct lkpi_sta *lsta;
struct mbufq mq;
struct mbuf *m;
+ bool shall_tx;
lsta = ctx;
@@ -3780,9 +5221,28 @@ lkpi_80211_txq_task(void *ctx, int pending)
LKPI_80211_LSTA_TXQ_LOCK(lsta);
/*
* Do not re-check lsta->txq_ready here; we may have a pending
- * disassoc frame still.
+ * disassoc/deauth frame still. On the contrary if txq_ready is
+ * false we do not have a valid sta anymore in the firmware so no
+ * point to try to TX.
+ * We also use txq_ready as a semaphore and will drain the txq manually
+ * if needed on our way towards SCAN/INIT in the state machine.
+ */
+#if 0
+ shall_tx = lsta->added_to_drv && lsta->txq_ready;
+#else
+ /*
+ * Backout this part of 886653492945f which breaks rtw88 or
+ * in general drivers without (*sta_state)() but only the
+ * legacy fallback to (*sta_add)().
+ */
+ shall_tx = lsta->txq_ready;
+#endif
+ if (__predict_true(shall_tx))
+ mbufq_concat(&mq, &lsta->txq);
+ /*
+ * else a state change will push the packets out manually or
+ * lkpi_lsta_free() will drain the lsta->txq and free the mbufs.
*/
- mbufq_concat(&mq, &lsta->txq);
LKPI_80211_LSTA_TXQ_UNLOCK(lsta);
m = mbufq_dequeue(&mq);
@@ -3803,7 +5263,7 @@ lkpi_ic_transmit(struct ieee80211com *ic, struct mbuf *m)
struct ieee80211_node *ni;
ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
- return (lkpi_ic_raw_xmit(ni, m, NULL));
+ return (lkpi_xmit(ni, m, NULL, false));
}
#ifdef LKPI_80211_HT
@@ -3817,7 +5277,7 @@ lkpi_ic_recv_action(struct ieee80211_node *ni, const struct ieee80211_frame *wh,
ic = ni->ni_ic;
lhw = ic->ic_softc;
- IMPROVE_HT();
+ IMPROVE_HT("recv_action called; nothing to do in lkpi; make debugging");
return (lhw->ic_recv_action(ni, wh, frm, efrm));
}
@@ -3831,7 +5291,7 @@ lkpi_ic_send_action(struct ieee80211_node *ni, int category, int action, void *s
ic = ni->ni_ic;
lhw = ic->ic_softc;
- IMPROVE_HT();
+ IMPROVE_HT("send_action called; nothing to do in lkpi; make debugging");
return (lhw->ic_send_action(ni, category, action, sa));
}
@@ -3846,52 +5306,207 @@ lkpi_ic_ampdu_enable(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap)
ic = ni->ni_ic;
lhw = ic->ic_softc;
- IMPROVE_HT();
+ IMPROVE_HT("ieee80211_ampdu_enable called; nothing to do in lkpi for now; make debugging");
return (lhw->ic_ampdu_enable(ni, tap));
}
+/*
+ * (*ic_addba_request)() is called by ieee80211_ampdu_request() before
+ * calling send_action(CAT_BA, BA_ADDBA_REQUEST).
+ *
+ * NB: returns 0 on ERROR!
+ */
static int
lkpi_ic_addba_request(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
int dialogtoken, int baparamset, int batimeout)
{
struct ieee80211com *ic;
struct lkpi_hw *lhw;
+ struct ieee80211_hw *hw;
+ struct ieee80211vap *vap;
+ struct lkpi_vif *lvif;
+ struct ieee80211_vif *vif;
+ struct lkpi_sta *lsta;
+ struct ieee80211_sta *sta;
+ struct ieee80211_ampdu_params params = { };
+ int error;
ic = ni->ni_ic;
lhw = ic->ic_softc;
+ hw = LHW_TO_HW(lhw);
+ vap = ni->ni_vap;
+ lvif = VAP_TO_LVIF(vap);
+ vif = LVIF_TO_VIF(lvif);
+ lsta = ni->ni_drv_data;
+ sta = LSTA_TO_STA(lsta);
- IMPROVE_HT();
+ if (!lsta->added_to_drv) {
+ ic_printf(ic, "%s: lsta %p ni %p, sta %p not added to firmware\n",
+ __func__, lsta, ni, sta);
+ return (0);
+ }
+
+ params.sta = sta;
+ params.action = IEEE80211_AMPDU_TX_START;
+ /* Keep 0 here! */
+ params.buf_size = 0;
+ params.timeout = 0;
+ params.ssn = tap->txa_start & (IEEE80211_SEQ_RANGE-1);
+ params.tid = tap->txa_tid;
+ params.amsdu = false;
+
+ IEEE80211_UNLOCK(ic);
+ wiphy_lock(hw->wiphy);
+ error = lkpi_80211_mo_ampdu_action(hw, vif, &params);
+ wiphy_unlock(hw->wiphy);
+ IEEE80211_LOCK(ic);
+ if (error != 0) {
+ ic_printf(ic, "%s: mo_ampdu_action returned %d. ni %p tap %p\n",
+ __func__, error, ni, tap);
+ return (0);
+ }
return (lhw->ic_addba_request(ni, tap, dialogtoken, baparamset, batimeout));
}
+/*
+ * (*ic_addba_response)() is called from ht_recv_action_ba_addba_response()
+ * and calls the default ieee80211_addba_response() which always returns 1.
+ *
+ * NB: No error checking in net80211!
+ * Staying with 0 is an error.
+ */
static int
lkpi_ic_addba_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
int status, int baparamset, int batimeout)
{
struct ieee80211com *ic;
struct lkpi_hw *lhw;
+ struct ieee80211_hw *hw;
+ struct ieee80211vap *vap;
+ struct lkpi_vif *lvif;
+ struct ieee80211_vif *vif;
+ struct lkpi_sta *lsta;
+ struct ieee80211_sta *sta;
+ struct ieee80211_ampdu_params params = { };
+ int error;
ic = ni->ni_ic;
lhw = ic->ic_softc;
+ hw = LHW_TO_HW(lhw);
+ vap = ni->ni_vap;
+ lvif = VAP_TO_LVIF(vap);
+ vif = LVIF_TO_VIF(lvif);
+ lsta = ni->ni_drv_data;
+ sta = LSTA_TO_STA(lsta);
- IMPROVE_HT();
+ if (!lsta->added_to_drv) {
+ ic_printf(ic, "%s: lsta %p ni %p, sta %p not added to firmware\n",
+ __func__, lsta, ni, sta);
+ return (0);
+ }
+
+ if (status == IEEE80211_STATUS_SUCCESS) {
+ params.sta = sta;
+ params.action = IEEE80211_AMPDU_TX_OPERATIONAL;
+ params.buf_size = tap->txa_wnd;
+ params.timeout = 0;
+ params.ssn = 0;
+ params.tid = tap->txa_tid;
+ if ((tap->txa_flags & IEEE80211_AGGR_AMSDU) != 0)
+ params.amsdu = true;
+ else
+ params.amsdu = false;
+ } else {
+ /* We need to free the allocated resources. */
+ params.sta = sta;
+ switch (status) {
+ /* params.action = FLUSH, FLUSH_CONT */
+ default:
+ params.action = IEEE80211_AMPDU_TX_STOP_CONT;
+ break;
+ }
+ params.buf_size = 0;
+ params.timeout = 0;
+ params.ssn = 0;
+ params.tid = tap->txa_tid;
+ params.amsdu = false;
+ }
+
+ IEEE80211_UNLOCK(ic);
+ wiphy_lock(hw->wiphy);
+ error = lkpi_80211_mo_ampdu_action(hw, vif, &params);
+ wiphy_unlock(hw->wiphy);
+ IEEE80211_LOCK(ic);
+ if (error != 0) {
+ ic_printf(ic, "%s: mo_ampdu_action returned %d. ni %p tap %p\n",
+ __func__, error, ni, tap);
+ return (0);
+ }
+
+ IMPROVE_HT("who unleashes the TXQ? and when?, do we need to ni->ni_txseqs[tid] = tap->txa_start & 0xfff;");
return (lhw->ic_addba_response(ni, tap, status, baparamset, batimeout));
}
+/*
+ * (*ic_addba_stop)() is called from ampdu_tx_stop(), ht_recv_action_ba_delba(),
+ * and ieee80211_ampdu_stop() and calls the default ieee80211_addba_stop().
+ */
static void
lkpi_ic_addba_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap)
{
struct ieee80211com *ic;
struct lkpi_hw *lhw;
+ struct ieee80211_hw *hw;
+ struct ieee80211vap *vap;
+ struct lkpi_vif *lvif;
+ struct ieee80211_vif *vif;
+ struct lkpi_sta *lsta;
+ struct ieee80211_sta *sta;
+ struct ieee80211_ampdu_params params = { };
+ int error;
ic = ni->ni_ic;
lhw = ic->ic_softc;
+ hw = LHW_TO_HW(lhw);
+ vap = ni->ni_vap;
+ lvif = VAP_TO_LVIF(vap);
+ vif = LVIF_TO_VIF(lvif);
+ lsta = ni->ni_drv_data;
+ sta = LSTA_TO_STA(lsta);
- IMPROVE_HT();
+ if (!lsta->added_to_drv) {
+ ic_printf(ic, "%s: lsta %p ni %p, sta %p not added to firmware\n",
+ __func__, lsta, ni, sta);
+ goto n80211;
+ }
+
+ /* We need to free the allocated resources. */
+ params.sta = sta;
+ IMPROVE("net80211 does not provide a reason to us");
+ params.action = IEEE80211_AMPDU_TX_STOP_CONT; /* params.action = FLUSH, FLUSH_CONT */
+ params.buf_size = 0;
+ params.timeout = 0;
+ params.ssn = 0;
+ params.tid = tap->txa_tid;
+ params.amsdu = false;
+
+ IEEE80211_UNLOCK(ic);
+ wiphy_lock(hw->wiphy);
+ error = lkpi_80211_mo_ampdu_action(hw, vif, &params);
+ wiphy_unlock(hw->wiphy);
+ IEEE80211_LOCK(ic);
+ if (error != 0) {
+ ic_printf(ic, "%s: mo_ampdu_action returned %d. ni %p tap %p\n",
+ __func__, error, ni, tap);
+ goto n80211;
+ }
+ IMPROVE_HT("anyting else?");
+
+n80211:
lhw->ic_addba_stop(ni, tap);
}
@@ -3935,8 +5550,8 @@ lkpi_ic_ampdu_rx_start(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap
struct lkpi_vif *lvif;
struct ieee80211_vif *vif;
struct lkpi_sta *lsta;
- struct ieee80211_sta *sta;
- struct ieee80211_ampdu_params params;
+ struct ieee80211_sta *sta;
+ struct ieee80211_ampdu_params params = { };
int error;
ic = ni->ni_ic;
@@ -3948,6 +5563,14 @@ lkpi_ic_ampdu_rx_start(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap
lsta = ni->ni_drv_data;
sta = LSTA_TO_STA(lsta);
+ IEEE80211_UNLOCK_ASSERT(ic);
+
+ if (!lsta->added_to_drv) {
+ ic_printf(ic, "%s: lsta %p ni %p vap %p, sta %p not added to firmware\n",
+ __func__, lsta, ni, vap, sta);
+ return (-ENXIO);
+ }
+
params.sta = sta;
params.action = IEEE80211_AMPDU_RX_START;
params.buf_size = _IEEE80211_MASKSHIFT(le16toh(baparamset), IEEE80211_BAPS_BUFSIZ);
@@ -3955,22 +5578,33 @@ lkpi_ic_ampdu_rx_start(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap
params.buf_size = IEEE80211_MAX_AMPDU_BUF_HT;
else
params.buf_size = min(params.buf_size, IEEE80211_MAX_AMPDU_BUF_HT);
- if (params.buf_size > hw->max_rx_aggregation_subframes)
+ if (hw->max_rx_aggregation_subframes > 0 &&
+ params.buf_size > hw->max_rx_aggregation_subframes)
params.buf_size = hw->max_rx_aggregation_subframes;
params.timeout = le16toh(batimeout);
params.ssn = _IEEE80211_MASKSHIFT(le16toh(baseqctl), IEEE80211_BASEQ_START);
params.tid = _IEEE80211_MASKSHIFT(le16toh(baparamset), IEEE80211_BAPS_TID);
- params.amsdu = false;
- IMPROVE_HT("Do we need to distinguish based on SUPPORTS_REORDERING_BUFFER?");
+ /* Based on net80211::ampdu_rx_start(). */
+ if ((vap->iv_htcaps & IEEE80211_HTC_RX_AMSDU_AMPDU) &&
+ (_IEEE80211_MASKSHIFT(baparamset, IEEE80211_BAPS_AMSDU)))
+ params.amsdu = true;
+ else
+ params.amsdu = false;
- /* This may call kalloc. Make sure we can sleep. */
+ wiphy_lock(hw->wiphy);
error = lkpi_80211_mo_ampdu_action(hw, vif, &params);
+ wiphy_unlock(hw->wiphy);
if (error != 0) {
ic_printf(ic, "%s: mo_ampdu_action returned %d. ni %p rap %p\n",
__func__, error, ni, rap);
return (error);
}
+
+ if (!ieee80211_hw_check(hw, SUPPORTS_REORDERING_BUFFER)) {
+ IMPROVE("%s: TODO: SUPPORTS_REORDERING_BUFFER not set; check net80211\n", __func__);
+ }
+
IMPROVE_HT("net80211 is missing the error check on return and assumes success");
error = lhw->ic_ampdu_rx_start(ni, rap, baparamset, batimeout, baseqctl);
@@ -3987,10 +5621,11 @@ lkpi_ic_ampdu_rx_stop(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap)
struct lkpi_vif *lvif;
struct ieee80211_vif *vif;
struct lkpi_sta *lsta;
- struct ieee80211_sta *sta;
- struct ieee80211_ampdu_params params;
+ struct ieee80211_sta *sta;
+ struct ieee80211_ampdu_params params = { };
int error;
uint8_t tid;
+ bool ic_locked;
ic = ni->ni_ic;
lhw = ic->ic_softc;
@@ -4028,7 +5663,14 @@ lkpi_ic_ampdu_rx_stop(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap)
params.tid = tid;
params.amsdu = false;
+ ic_locked = IEEE80211_IS_LOCKED(ic);
+ if (ic_locked)
+ IEEE80211_UNLOCK(ic);
+ wiphy_lock(hw->wiphy);
error = lkpi_80211_mo_ampdu_action(hw, vif, &params);
+ wiphy_unlock(hw->wiphy);
+ if (ic_locked)
+ IEEE80211_LOCK(ic);
if (error != 0)
ic_printf(ic, "%s: mo_ampdu_action returned %d. ni %p rap %p\n",
__func__, error, ni, rap);
@@ -4174,11 +5816,13 @@ lkpi_ic_getradiocaps(struct ieee80211com *ic, int maxchan,
NL80211_BAND_5GHZ);
#ifdef LKPI_80211_VHT
- if (hw->wiphy->bands[NL80211_BAND_5GHZ]->vht_cap.vht_supported){
+ if (hw->wiphy->bands[NL80211_BAND_5GHZ]->vht_cap.vht_supported) {
ic->ic_flags_ext |= IEEE80211_FEXT_VHT;
ic->ic_vht_cap.vht_cap_info =
hw->wiphy->bands[NL80211_BAND_5GHZ]->vht_cap.cap;
+ ic->ic_vht_cap.supp_mcs =
+ hw->wiphy->bands[NL80211_BAND_5GHZ]->vht_cap.vht_mcs;
setbit(bands, IEEE80211_MODE_VHT_5GHZ);
chan_flags |= NET80211_CBW_FLAG_VHT80;
@@ -4238,8 +5882,6 @@ lkpi_ieee80211_ifalloc(void)
struct ieee80211com *ic;
ic = malloc(sizeof(*ic), M_LKPI80211, M_WAITOK | M_ZERO);
- if (ic == NULL)
- return (NULL);
/* Setting these happens later when we have device information. */
ic->ic_softc = NULL;
@@ -4264,7 +5906,6 @@ linuxkpi_ieee80211_alloc_hw(size_t priv_len, const struct ieee80211_ops *ops)
lhw = wiphy_priv(wiphy);
lhw->ops = ops;
- LKPI_80211_LHW_LOCK_INIT(lhw);
LKPI_80211_LHW_SCAN_LOCK_INIT(lhw);
LKPI_80211_LHW_TXQ_LOCK_INIT(lhw);
sx_init_flags(&lhw->lvif_sx, "lhw-lvif", SX_RECURSE | SX_DUPOK);
@@ -4274,10 +5915,13 @@ linuxkpi_ieee80211_alloc_hw(size_t priv_len, const struct ieee80211_ops *ops)
TAILQ_INIT(&lhw->scheduled_txqs[ac]);
}
+ /* Chanctx_conf */
+ INIT_LIST_HEAD(&lhw->lchanctx_list);
+
/* Deferred RX path. */
LKPI_80211_LHW_RXQ_LOCK_INIT(lhw);
TASK_INIT(&lhw->rxq_task, 0, lkpi_80211_lhw_rxq_task, lhw);
- mbufq_init(&lhw->rxq, IFQ_MAXLEN);
+ mbufq_init(&lhw->rxq, 32 * NAPI_POLL_WEIGHT);
lhw->rxq_stopped = false;
/*
@@ -4291,10 +5935,6 @@ linuxkpi_ieee80211_alloc_hw(size_t priv_len, const struct ieee80211_ops *ops)
/* BSD Specific. */
lhw->ic = lkpi_ieee80211_ifalloc();
- if (lhw->ic == NULL) {
- ieee80211_free_hw(hw);
- return (NULL);
- }
IMPROVE();
@@ -4325,6 +5965,7 @@ linuxkpi_ieee80211_iffree(struct ieee80211_hw *hw)
/* Flush mbufq (make sure to release ni refs!). */
m = mbufq_dequeue(&lhw->rxq);
while (m != NULL) {
+#ifdef LKPI_80211_USE_MTAG
struct m_tag *mtag;
mtag = m_tag_locate(m, MTAG_ABI_LKPI80211, LKPI80211_TAG_RXNI, NULL);
@@ -4334,6 +5975,14 @@ linuxkpi_ieee80211_iffree(struct ieee80211_hw *hw)
rxni = (struct lkpi_80211_tag_rxni *)(mtag + 1);
ieee80211_free_node(rxni->ni);
}
+#else
+ if (m->m_pkthdr.PH_loc.ptr != NULL) {
+ struct ieee80211_node *ni;
+
+ ni = m->m_pkthdr.PH_loc.ptr;
+ ieee80211_free_node(ni);
+ }
+#endif
m_freem(m);
m = mbufq_dequeue(&lhw->rxq);
}
@@ -4341,10 +5990,25 @@ linuxkpi_ieee80211_iffree(struct ieee80211_hw *hw)
__func__, lhw, mbufq_len(&lhw->rxq)));
LKPI_80211_LHW_RXQ_LOCK_DESTROY(lhw);
+ /* Chanctx_conf. */
+ if (!list_empty_careful(&lhw->lchanctx_list)) {
+ struct lkpi_chanctx *lchanctx, *next;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+
+ list_for_each_entry_safe(lchanctx, next, &lhw->lchanctx_list, entry) {
+ if (lchanctx->added_to_drv) {
+ /* In reality we should panic? */
+ chanctx_conf = &lchanctx->chanctx_conf;
+ lkpi_80211_mo_remove_chanctx(hw, chanctx_conf);
+ }
+ list_del(&lchanctx->entry);
+ free(lchanctx, M_LKPI80211);
+ }
+ }
+
/* Cleanup more of lhw here or in wiphy_free()? */
LKPI_80211_LHW_TXQ_LOCK_DESTROY(lhw);
LKPI_80211_LHW_SCAN_LOCK_DESTROY(lhw);
- LKPI_80211_LHW_LOCK_DESTROY(lhw);
sx_destroy(&lhw->lvif_sx);
IMPROVE();
}
@@ -4453,6 +6117,10 @@ linuxkpi_ieee80211_ifattach(struct ieee80211_hw *hw)
lhw->scan_flags |= LKPI_LHW_SCAN_HW;
}
+ /* Does HW support Fragmentation offload? */
+ if (ieee80211_hw_check(hw, SUPPORTS_TX_FRAG))
+ ic->ic_flags_ext |= IEEE80211_FEXT_FRAG_OFFLOAD;
+
/*
* The wiphy variables report bitmasks of avail antennas.
* (*get_antenna) get the current bitmask sets which can be
@@ -4471,10 +6139,46 @@ linuxkpi_ieee80211_ifattach(struct ieee80211_hw *hw)
ic->ic_cryptocaps = 0;
#ifdef LKPI_80211_HW_CRYPTO
- if (hw->wiphy->n_cipher_suites > 0) {
- for (i = 0; i < hw->wiphy->n_cipher_suites; i++)
- ic->ic_cryptocaps |= lkpi_l80211_to_net80211_cyphers(
- hw->wiphy->cipher_suites[i]);
+ if (lkpi_hwcrypto && hw->wiphy->n_cipher_suites > 0) {
+ uint32_t hwciphers;
+
+ hwciphers = 0;
+ for (i = 0; i < hw->wiphy->n_cipher_suites; i++) {
+ uint32_t cs;
+
+ cs = lkpi_l80211_to_net80211_cyphers(
+ ic, hw->wiphy->cipher_suites[i]);
+ if (cs == IEEE80211_CRYPTO_TKIP) {
+ /*
+ * We do set this here. We will only find out
+ * when doing a SET_KEY operation depending on
+ * what the driver returns.
+ * net80211::ieee80211_crypto_newkey()
+ * checks this so we will have to do flags
+ * surgery later.
+ */
+ cs |= IEEE80211_CRYPTO_TKIPMIC;
+ }
+ hwciphers |= cs;
+ }
+ /*
+ * (20250415) nothing anywhere in the path checks we actually
+ * support all these in net80211.
+ * net80211 supports _256 variants but the ioctl does not.
+ */
+ IMPROVE("as net80211 grows more support, enable them");
+ hwciphers &= (IEEE80211_CRYPTO_WEP |
+ IEEE80211_CRYPTO_TKIP | IEEE80211_CRYPTO_TKIPMIC |
+ IEEE80211_CRYPTO_AES_CCM | IEEE80211_CRYPTO_AES_GCM_128);
+ /*
+ * We only support CCMP here, so further filter.
+ * Also permit TKIP if turned on.
+ */
+ hwciphers &= (IEEE80211_CRYPTO_AES_CCM |
+ IEEE80211_CRYPTO_AES_GCM_128 |
+ (lkpi_hwcrypto_tkip ? (IEEE80211_CRYPTO_TKIP |
+ IEEE80211_CRYPTO_TKIPMIC) : 0));
+ ieee80211_set_hardware_ciphers(ic, hwciphers);
}
#endif
@@ -4512,30 +6216,36 @@ linuxkpi_ieee80211_ifattach(struct ieee80211_hw *hw)
ic->ic_node_free = lkpi_ic_node_free;
#ifdef LKPI_80211_HT
- lhw->ic_recv_action = ic->ic_recv_action;
- ic->ic_recv_action = lkpi_ic_recv_action;
- lhw->ic_send_action = ic->ic_send_action;
- ic->ic_send_action = lkpi_ic_send_action;
-
- lhw->ic_ampdu_enable = ic->ic_ampdu_enable;
- ic->ic_ampdu_enable = lkpi_ic_ampdu_enable;
-
- lhw->ic_addba_request = ic->ic_addba_request;
- ic->ic_addba_request = lkpi_ic_addba_request;
- lhw->ic_addba_response = ic->ic_addba_response;
- ic->ic_addba_response = lkpi_ic_addba_response;
- lhw->ic_addba_stop = ic->ic_addba_stop;
- ic->ic_addba_stop = lkpi_ic_addba_stop;
- lhw->ic_addba_response_timeout = ic->ic_addba_response_timeout;
- ic->ic_addba_response_timeout = lkpi_ic_addba_response_timeout;
-
- lhw->ic_bar_response = ic->ic_bar_response;
- ic->ic_bar_response = lkpi_ic_bar_response;
-
- lhw->ic_ampdu_rx_start = ic->ic_ampdu_rx_start;
- ic->ic_ampdu_rx_start = lkpi_ic_ampdu_rx_start;
- lhw->ic_ampdu_rx_stop = ic->ic_ampdu_rx_stop;
- ic->ic_ampdu_rx_stop = lkpi_ic_ampdu_rx_stop;
+ /*
+ * Only attach if the driver/firmware supports (*ampdu_action)().
+ * Otherwise it is in the hands of net80211.
+ */
+ if (lhw->ops->ampdu_action != NULL) {
+ lhw->ic_recv_action = ic->ic_recv_action;
+ ic->ic_recv_action = lkpi_ic_recv_action;
+ lhw->ic_send_action = ic->ic_send_action;
+ ic->ic_send_action = lkpi_ic_send_action;
+
+ lhw->ic_ampdu_enable = ic->ic_ampdu_enable;
+ ic->ic_ampdu_enable = lkpi_ic_ampdu_enable;
+
+ lhw->ic_addba_request = ic->ic_addba_request;
+ ic->ic_addba_request = lkpi_ic_addba_request;
+ lhw->ic_addba_response = ic->ic_addba_response;
+ ic->ic_addba_response = lkpi_ic_addba_response;
+ lhw->ic_addba_stop = ic->ic_addba_stop;
+ ic->ic_addba_stop = lkpi_ic_addba_stop;
+ lhw->ic_addba_response_timeout = ic->ic_addba_response_timeout;
+ ic->ic_addba_response_timeout = lkpi_ic_addba_response_timeout;
+
+ lhw->ic_bar_response = ic->ic_bar_response;
+ ic->ic_bar_response = lkpi_ic_bar_response;
+
+ lhw->ic_ampdu_rx_start = ic->ic_ampdu_rx_start;
+ ic->ic_ampdu_rx_start = lkpi_ic_ampdu_rx_start;
+ lhw->ic_ampdu_rx_stop = ic->ic_ampdu_rx_stop;
+ ic->ic_ampdu_rx_stop = lkpi_ic_ampdu_rx_stop;
+ }
#endif
lkpi_radiotap_attach(lhw);
@@ -4570,7 +6280,7 @@ linuxkpi_ieee80211_ifattach(struct ieee80211_hw *hw)
cfg80211_chandef_create(&hw->conf.chandef, &channels[i],
#ifdef LKPI_80211_HT
- (ic->ic_htcaps & IEEE80211_HTC_HT) ? 0 :
+ (ic->ic_flags_ht & IEEE80211_FHT_HT) ? NL80211_CHAN_HT20 :
#endif
NL80211_CHAN_NO_HT);
break;
@@ -4611,7 +6321,7 @@ linuxkpi_ieee80211_ifattach(struct ieee80211_hw *hw)
lhw->scan_ie_len += sizeof(struct ieee80211_ie_htcap);
#endif
#if defined(LKPI_80211_VHT)
- if ((ic->ic_flags_ext & IEEE80211_FEXT_VHT) != 0)
+ if (IEEE80211_CONF_VHT(ic))
lhw->scan_ie_len += 2 + sizeof(struct ieee80211_vht_cap);
#endif
@@ -4704,15 +6414,63 @@ linuxkpi_ieee80211_iterate_interfaces(struct ieee80211_hw *hw,
LKPI_80211_LHW_LVIF_UNLOCK(lhw);
}
+static void
+lkpi_ieee80211_iterate_keys(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ ieee80211_keyix keyix, struct lkpi_sta *lsta,
+ void(*iterfunc)(struct ieee80211_hw *, struct ieee80211_vif *,
+ struct ieee80211_sta *, struct ieee80211_key_conf *, void *),
+ void *arg)
+{
+ if (!lsta->added_to_drv)
+ return;
+
+ if (lsta->kc[keyix] == NULL)
+ return;
+
+ iterfunc(hw, vif, LSTA_TO_STA(lsta), lsta->kc[keyix], arg);
+}
+
void
linuxkpi_ieee80211_iterate_keys(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
void(*iterfunc)(struct ieee80211_hw *, struct ieee80211_vif *,
struct ieee80211_sta *, struct ieee80211_key_conf *, void *),
- void *arg)
+ void *arg, bool rcu)
{
+ struct lkpi_sta *lsta;
+ struct lkpi_vif *lvif;
- UNIMPLEMENTED;
+ lvif = VIF_TO_LVIF(vif);
+
+ if (rcu) {
+ rcu_read_lock_held(); /* XXX-BZ is this correct? */
+
+ if (vif == NULL) {
+ TODO();
+ } else {
+ list_for_each_entry_rcu(lsta, &lvif->lsta_list, lsta_list) {
+ for (ieee80211_keyix keyix = 0; keyix < nitems(lsta->kc);
+ keyix++)
+ lkpi_ieee80211_iterate_keys(hw, vif,
+ keyix, lsta, iterfunc, arg);
+ }
+ }
+ } else {
+ TODO("Used by suspend/resume; order of keys as installed to "
+ "firmware is important; we'll need to rewrite some code for that");
+ lockdep_assert_wiphy(hw->wiphy);
+
+ if (vif == NULL) {
+ TODO();
+ } else {
+ list_for_each_entry(lsta, &lvif->lsta_list, lsta_list) {
+ for (ieee80211_keyix keyix = 0; keyix < nitems(lsta->kc);
+ keyix++)
+ lkpi_ieee80211_iterate_keys(hw, vif,
+ keyix, lsta, iterfunc, arg);
+ }
+ }
+ }
}
void
@@ -4722,8 +6480,6 @@ linuxkpi_ieee80211_iterate_chan_contexts(struct ieee80211_hw *hw,
void *arg)
{
struct lkpi_hw *lhw;
- struct lkpi_vif *lvif;
- struct ieee80211_vif *vif;
struct lkpi_chanctx *lchanctx;
KASSERT(hw != NULL && iterfunc != NULL,
@@ -4731,22 +6487,13 @@ linuxkpi_ieee80211_iterate_chan_contexts(struct ieee80211_hw *hw,
lhw = HW_TO_LHW(hw);
- IMPROVE("lchanctx should be its own list somewhere");
-
- LKPI_80211_LHW_LVIF_LOCK(lhw);
- TAILQ_FOREACH(lvif, &lhw->lvif_head, lvif_entry) {
-
- vif = LVIF_TO_VIF(lvif);
- if (vif->chanctx_conf == NULL)
- continue;
-
- lchanctx = CHANCTX_CONF_TO_LCHANCTX(vif->chanctx_conf);
+ rcu_read_lock();
+ list_for_each_entry_rcu(lchanctx, &lhw->lchanctx_list, entry) {
if (!lchanctx->added_to_drv)
continue;
-
- iterfunc(hw, &lchanctx->conf, arg);
+ iterfunc(hw, &lchanctx->chanctx_conf, arg);
}
- LKPI_80211_LHW_LVIF_UNLOCK(lhw);
+ rcu_read_unlock();
}
void
@@ -4766,14 +6513,14 @@ linuxkpi_ieee80211_iterate_stations_atomic(struct ieee80211_hw *hw,
LKPI_80211_LHW_LVIF_LOCK(lhw);
TAILQ_FOREACH(lvif, &lhw->lvif_head, lvif_entry) {
- LKPI_80211_LVIF_LOCK(lvif);
- TAILQ_FOREACH(lsta, &lvif->lsta_head, lsta_entry) {
+ rcu_read_lock();
+ list_for_each_entry_rcu(lsta, &lvif->lsta_list, lsta_list) {
if (!lsta->added_to_drv)
continue;
sta = LSTA_TO_STA(lsta);
iterfunc(arg, sta);
}
- LKPI_80211_LVIF_UNLOCK(lvif);
+ rcu_read_unlock();
}
LKPI_80211_LHW_LVIF_UNLOCK(lhw);
}
@@ -4839,10 +6586,13 @@ static void
lkpi_80211_lhw_rxq_rx_one(struct lkpi_hw *lhw, struct mbuf *m)
{
struct ieee80211_node *ni;
+#ifdef LKPI_80211_USE_MTAG
struct m_tag *mtag;
+#endif
int ok;
ni = NULL;
+#ifdef LKPI_80211_USE_MTAG
mtag = m_tag_locate(m, MTAG_ABI_LKPI80211, LKPI80211_TAG_RXNI, NULL);
if (mtag != NULL) {
struct lkpi_80211_tag_rxni *rxni;
@@ -4850,6 +6600,12 @@ lkpi_80211_lhw_rxq_rx_one(struct lkpi_hw *lhw, struct mbuf *m)
rxni = (struct lkpi_80211_tag_rxni *)(mtag + 1);
ni = rxni->ni;
}
+#else
+ if (m->m_pkthdr.PH_loc.ptr != NULL) {
+ ni = m->m_pkthdr.PH_loc.ptr;
+ m->m_pkthdr.PH_loc.ptr = NULL;
+ }
+#endif
if (ni != NULL) {
ok = ieee80211_input_mimo(ni, m);
@@ -4863,7 +6619,7 @@ lkpi_80211_lhw_rxq_rx_one(struct lkpi_hw *lhw, struct mbuf *m)
#ifdef LINUXKPI_DEBUG_80211
if (linuxkpi_debug_80211 & D80211_TRACE_RX)
- printf("TRACE %s: handled frame type %#0x\n", __func__, ok);
+ printf("TRACE-RX: %s: handled frame type %#0x\n", __func__, ok);
#endif
}
@@ -4878,8 +6634,8 @@ lkpi_80211_lhw_rxq_task(void *ctx, int pending)
#ifdef LINUXKPI_DEBUG_80211
if (linuxkpi_debug_80211 & D80211_TRACE_RX)
- printf("%s:%d lhw %p pending %d mbuf_qlen %d\n",
- __func__, __LINE__, lhw, pending, mbufq_len(&lhw->rxq));
+ printf("TRACE-RX: %s: lhw %p pending %d mbuf_qlen %d\n",
+ __func__, lhw, pending, mbufq_len(&lhw->rxq));
#endif
mbufq_init(&mq, IFQ_MAXLEN);
@@ -4895,6 +6651,193 @@ lkpi_80211_lhw_rxq_task(void *ctx, int pending)
}
}
+static void
+lkpi_convert_rx_status(struct ieee80211_hw *hw, struct lkpi_sta *lsta,
+ struct ieee80211_rx_status *rx_status,
+ struct ieee80211_rx_stats *rx_stats,
+ uint8_t *rssip)
+{
+ struct ieee80211_supported_band *supband;
+ struct rate_info rxrate;
+ int i;
+ uint8_t rssi;
+
+ memset(&rxrate, 0, sizeof(rxrate));
+ memset(rx_stats, 0, sizeof(*rx_stats));
+ rx_stats->r_flags = IEEE80211_R_NF | IEEE80211_R_RSSI;
+ /* XXX-BZ correct hardcoded noise floor, survey data? */
+ rx_stats->c_nf = -96;
+ if (ieee80211_hw_check(hw, SIGNAL_DBM) &&
+ !(rx_status->flag & RX_FLAG_NO_SIGNAL_VAL))
+ rssi = rx_status->signal;
+ else
+ rssi = rx_stats->c_nf;
+ /*
+ * net80211 signal strength data are in .5 dBm units relative to
+ * the current noise floor (see comment in ieee80211_node.h).
+ */
+ rssi -= rx_stats->c_nf;
+ if (rssip != NULL)
+ *rssip = rssi;
+ rx_stats->c_rssi = rssi * 2;
+ rx_stats->r_flags |= IEEE80211_R_BAND;
+ rx_stats->c_band =
+ lkpi_nl80211_band_to_net80211_band(rx_status->band);
+ rx_stats->r_flags |= IEEE80211_R_FREQ | IEEE80211_R_IEEE;
+ rx_stats->c_freq = rx_status->freq;
+ rx_stats->c_ieee = ieee80211_mhz2ieee(rx_stats->c_freq, rx_stats->c_band);
+
+ rx_stats->c_rx_tsf = rx_status->mactime;
+
+ /* XXX RX_FLAG_MACTIME_IS_RTAP_TS64 ? */
+ if ((rx_status->flag & RX_FLAG_MACTIME) ==
+ (RX_FLAG_MACTIME_START|RX_FLAG_MACTIME_END)) {
+ rx_stats->r_flags |= IEEE80211_R_TSF64;
+ /* XXX RX_FLAG_MACTIME_PLCP_START ? */
+ if ((rx_status->flag & RX_FLAG_MACTIME) == RX_FLAG_MACTIME_START)
+ rx_stats->r_flags |= IEEE80211_R_TSF_START;
+ if ((rx_status->flag & RX_FLAG_MACTIME) == RX_FLAG_MACTIME_END)
+ rx_stats->r_flags |= IEEE80211_R_TSF_END;
+ /* XXX-BZ if TSF_END will net80211 do the unwind of time? */
+ }
+
+ if (rx_status->chains != 0) {
+ int cc;
+ int8_t crssi;
+
+ rx_stats->c_chain = rx_status->chains;
+ rx_stats->r_flags |= IEEE80211_R_C_CHAIN;
+
+ cc = 0;
+ for (i = 0; i < nitems(rx_status->chain_signal); i++) {
+ if (!(rx_status->chains & BIT(i)))
+ continue;
+ crssi = rx_status->chain_signal[i];
+ crssi -= rx_stats->c_nf;
+ rx_stats->c_rssi_ctl[i] = crssi * 2;
+ rx_stats->c_rssi_ext[i] = crssi * 2; /* XXX _ext ??? ATH thing? */
+ /* We currently only have the global noise floor value. */
+ rx_stats->c_nf_ctl[i] = rx_stats->c_nf;
+ rx_stats->c_nf_ext[i] = rx_stats->c_nf;
+ cc++;
+ }
+ if (cc > 0)
+ rx_stats->r_flags |= (IEEE80211_R_C_NF | IEEE80211_R_C_RSSI);
+ }
+
+ /* XXX-NET80211 We are not going to populate c_phytype! */
+
+ switch (rx_status->encoding) {
+ case RX_ENC_LEGACY:
+ {
+ uint32_t legacy = 0;
+
+ supband = hw->wiphy->bands[rx_status->band];
+ if (supband != NULL)
+ legacy = supband->bitrates[rx_status->rate_idx].bitrate;
+ rx_stats->c_rate = legacy;
+ rxrate.legacy = legacy;
+ /* Is there a LinuxKPI way of reporting IEEE80211_RX_F_CCK / _OFDM? */
+ break;
+ }
+ case RX_ENC_HT:
+ rx_stats->c_pktflags |= IEEE80211_RX_F_HT;
+ rx_stats->c_rate = rx_status->rate_idx; /* mcs */
+ rxrate.flags |= RATE_INFO_FLAGS_MCS;
+ rxrate.mcs = rx_status->rate_idx;
+ if ((rx_status->enc_flags & RX_ENC_FLAG_SHORT_GI) != 0) {
+ rx_stats->c_pktflags |= IEEE80211_RX_F_SHORTGI;
+ rxrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+ }
+ break;
+ case RX_ENC_VHT:
+ rx_stats->c_pktflags |= IEEE80211_RX_F_VHT;
+ rx_stats->c_rate = rx_status->rate_idx; /* mcs */
+ rx_stats->c_vhtnss = rx_status->nss;
+ rxrate.flags |= RATE_INFO_FLAGS_VHT_MCS;
+ rxrate.mcs = rx_status->rate_idx;
+ rxrate.nss = rx_status->nss;
+ if ((rx_status->enc_flags & RX_ENC_FLAG_SHORT_GI) != 0) {
+ rx_stats->c_pktflags |= IEEE80211_RX_F_SHORTGI;
+ rxrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+ }
+ break;
+ case RX_ENC_HE:
+ rxrate.flags |= RATE_INFO_FLAGS_HE_MCS;
+ rxrate.mcs = rx_status->rate_idx;
+ rxrate.nss = rx_status->nss;
+ /* XXX TODO */
+ TODO("net80211 has not matching encoding for %u", rx_status->encoding);
+ break;
+ case RX_ENC_EHT:
+ rxrate.flags |= RATE_INFO_FLAGS_EHT_MCS;
+ rxrate.mcs = rx_status->rate_idx;
+ rxrate.nss = rx_status->nss;
+ /* XXX TODO */
+ TODO("net80211 has not matching encoding for %u", rx_status->encoding);
+ break;
+ }
+
+ rxrate.bw = rx_status->bw;
+ switch (rx_status->bw) {
+ case RATE_INFO_BW_20:
+ rx_stats->c_width = IEEE80211_RX_FW_20MHZ;
+ break;
+ case RATE_INFO_BW_40:
+ rx_stats->c_width = IEEE80211_RX_FW_40MHZ;
+ break;
+ case RATE_INFO_BW_80:
+ rx_stats->c_width = IEEE80211_RX_FW_80MHZ;
+ break;
+ case RATE_INFO_BW_160:
+ rx_stats->c_width = IEEE80211_RX_FW_160MHZ;
+ break;
+ case RATE_INFO_BW_320:
+ case RATE_INFO_BW_HE_RU:
+ case RATE_INFO_BW_EHT_RU:
+ case RATE_INFO_BW_5:
+ case RATE_INFO_BW_10:
+ TODO("net80211 has not matching bandwidth for %u", rx_status->bw);
+ break;
+ }
+
+ if ((rx_status->enc_flags & RX_ENC_FLAG_LDPC) != 0)
+ rx_stats->c_pktflags |= IEEE80211_RX_F_LDPC;
+ if ((rx_status->enc_flags & RX_ENC_FLAG_STBC_MASK) != 0)
+ rx_stats->c_pktflags |= IEEE80211_RX_F_STBC;
+
+ /*
+ * We only need these for LKPI_80211_HW_CRYPTO in theory but in
+ * case the hardware does something we do not expect always leave
+ * these enabled. Leaving this commant as documentation for the || 1.
+ */
+#if defined(LKPI_80211_HW_CRYPTO) || 1
+ if (rx_status->flag & RX_FLAG_DECRYPTED) {
+ rx_stats->c_pktflags |= IEEE80211_RX_F_DECRYPTED;
+ /* Only valid if decrypted is set. */
+ if (rx_status->flag & RX_FLAG_PN_VALIDATED)
+ rx_stats->c_pktflags |= IEEE80211_RX_F_PN_VALIDATED;
+ }
+ if (rx_status->flag & RX_FLAG_IV_STRIPPED)
+ rx_stats->c_pktflags |= IEEE80211_RX_F_IV_STRIP;
+ if (rx_status->flag & RX_FLAG_ICV_STRIPPED)
+ rx_stats->c_pktflags |= IEEE80211_RX_F_ICV_STRIP;
+ if (rx_status->flag & RX_FLAG_MIC_STRIPPED)
+ rx_stats->c_pktflags |= IEEE80211_RX_F_MIC_STRIP;
+ if (rx_status->flag & RX_FLAG_MMIC_STRIPPED)
+ rx_stats->c_pktflags |= IEEE80211_RX_F_MMIC_STRIP;
+ if (rx_status->flag & RX_FLAG_MMIC_ERROR)
+ rx_stats->c_pktflags |= IEEE80211_RX_F_FAIL_MMIC;
+ if (rx_status->flag & RX_FLAG_FAILED_FCS_CRC)
+ rx_stats->c_pktflags |= IEEE80211_RX_F_FAIL_FCSCRC;
+#endif
+
+ if (lsta != NULL) {
+ memcpy(&lsta->sinfo.rxrate, &rxrate, sizeof(rxrate));
+ lsta->sinfo.filled |= BIT_ULL(NL80211_STA_INFO_RX_BITRATE);
+ }
+}
+
/* For %list see comment towards the end of the function. */
void
linuxkpi_ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
@@ -4911,12 +6854,16 @@ linuxkpi_ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
struct ieee80211vap *vap;
struct ieee80211_hdr *hdr;
struct lkpi_sta *lsta;
- int i, offset, ok;
- int8_t rssi;
+ int i, offset, ok, error;
+ uint8_t rssi;
bool is_beacon;
+ lhw = HW_TO_LHW(hw);
+ ic = lhw->ic;
+
if (skb->len < 2) {
/* Need 80211 stats here. */
+ counter_u64_add(ic->ic_ierrors, 1);
IMPROVE();
goto err;
}
@@ -4925,9 +6872,11 @@ linuxkpi_ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
* For now do the data copy; we can later improve things. Might even
* have an mbuf backing the skb data then?
*/
- m = m_get2(skb->len, M_NOWAIT, MT_DATA, M_PKTHDR);
- if (m == NULL)
+ m = m_get3(skb->len, M_NOWAIT, MT_DATA, M_PKTHDR);
+ if (m == NULL) {
+ counter_u64_add(ic->ic_ierrors, 1);
goto err;
+ }
m_copyback(m, 0, skb->tail - skb->data, skb->data);
shinfo = skb_shinfo(skb);
@@ -4949,9 +6898,9 @@ linuxkpi_ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
goto no_trace_beacons;
if (linuxkpi_debug_80211 & D80211_TRACE_RX)
- printf("TRACE-RX: %s: skb %p a/l/d/t-len (%u/%u/%u/%u) "
+ printf("TRACE-RX: %s: skb %p l/d/t-len (%u/%u/%u) "
"h %p d %p t %p e %p sh %p (%u) m %p plen %u len %u%s\n",
- __func__, skb, skb->_alloc_len, skb->len, skb->data_len,
+ __func__, skb, skb->len, skb->data_len,
skb->truesize, skb->head, skb->data, skb->tail, skb->end,
shinfo, shinfo->nr_frags,
m, m->m_pkthdr.len, m->m_len, is_beacon ? " beacon" : "");
@@ -4961,13 +6910,13 @@ linuxkpi_ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
/* Implement a dump_rxcb() !!! */
if (linuxkpi_debug_80211 & D80211_TRACE_RX)
- printf("TRACE %s: RXCB: %ju %ju %u, %#0x, %u, %#0x, %#0x, "
+ printf("TRACE-RX: %s: RXCB: %ju %ju %u, %b, %u, %#0x, %#0x, "
"%u band %u, %u { %d %d %d %d }, %d, %#x %#x %#x %#x %u %u %u\n",
__func__,
(uintmax_t)rx_status->boottime_ns,
(uintmax_t)rx_status->mactime,
rx_status->device_timestamp,
- rx_status->flag,
+ rx_status->flag, IEEE80211_RX_STATUS_FLAGS_BITS,
rx_status->freq,
rx_status->bw,
rx_status->encoding,
@@ -4989,41 +6938,7 @@ linuxkpi_ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
no_trace_beacons:
#endif
- memset(&rx_stats, 0, sizeof(rx_stats));
- rx_stats.r_flags = IEEE80211_R_NF | IEEE80211_R_RSSI;
- /* XXX-BZ correct hardcoded rssi and noise floor, how? survey? */
- rx_stats.c_nf = -96;
- if (ieee80211_hw_check(hw, SIGNAL_DBM) &&
- !(rx_status->flag & RX_FLAG_NO_SIGNAL_VAL))
- rssi = rx_status->signal;
- else
- rssi = rx_stats.c_nf;
- /*
- * net80211 signal strength data are in .5 dBm units relative to
- * the current noise floor (see comment in ieee80211_node.h).
- */
- rssi -= rx_stats.c_nf;
- rx_stats.c_rssi = rssi * 2;
- rx_stats.r_flags |= IEEE80211_R_BAND;
- rx_stats.c_band =
- lkpi_nl80211_band_to_net80211_band(rx_status->band);
- rx_stats.r_flags |= IEEE80211_R_FREQ | IEEE80211_R_IEEE;
- rx_stats.c_freq = rx_status->freq;
- rx_stats.c_ieee = ieee80211_mhz2ieee(rx_stats.c_freq, rx_stats.c_band);
-
- /* XXX (*sta_statistics)() to get to some of that? */
- /* XXX-BZ dump the FreeBSD version of rx_stats as well! */
-
- lhw = HW_TO_LHW(hw);
- ic = lhw->ic;
-
- ok = ieee80211_add_rx_params(m, &rx_stats);
- if (ok == 0) {
- m_freem(m);
- counter_u64_add(ic->ic_ierrors, 1);
- goto err;
- }
-
+ lsta = NULL;
if (sta != NULL) {
lsta = STA_TO_LSTA(sta);
ni = ieee80211_ref_node(lsta->ni);
@@ -5036,6 +6951,16 @@ no_trace_beacons:
lsta = ni->ni_drv_data;
}
+ rssi = 0;
+ lkpi_convert_rx_status(hw, lsta, rx_status, &rx_stats, &rssi);
+
+ ok = ieee80211_add_rx_params(m, &rx_stats);
+ if (ok == 0) {
+ m_freem(m);
+ counter_u64_add(ic->ic_ierrors, 1);
+ goto err;
+ }
+
if (ni != NULL)
vap = ni->ni_vap;
else
@@ -5047,7 +6972,7 @@ no_trace_beacons:
#ifdef LINUXKPI_DEBUG_80211
if (linuxkpi_debug_80211 & D80211_TRACE_RX)
- printf("TRACE %s: sta %p lsta %p state %d ni %p vap %p%s\n",
+ printf("TRACE-RX: %s: sta %p lsta %p state %d ni %p vap %p%s\n",
__func__, sta, lsta, (lsta != NULL) ? lsta->state : -1,
ni, vap, is_beacon ? " beacon" : "");
#endif
@@ -5118,32 +7043,47 @@ skip_device_ts:
}
#endif
- /*
- * Attach meta-information to the mbuf for the deferred RX path.
- * Currently this is best-effort. Should we need to be hard,
- * drop the frame and goto err;
- */
+ /* Attach meta-information to the mbuf for the deferred RX path. */
if (ni != NULL) {
+#ifdef LKPI_80211_USE_MTAG
struct m_tag *mtag;
struct lkpi_80211_tag_rxni *rxni;
mtag = m_tag_alloc(MTAG_ABI_LKPI80211, LKPI80211_TAG_RXNI,
sizeof(*rxni), IEEE80211_M_NOWAIT);
- if (mtag != NULL) {
- rxni = (struct lkpi_80211_tag_rxni *)(mtag + 1);
- rxni->ni = ni; /* We hold a reference. */
- m_tag_prepend(m, mtag);
+ if (mtag == NULL) {
+ m_freem(m);
+ counter_u64_add(ic->ic_ierrors, 1);
+ goto err;
}
+ rxni = (struct lkpi_80211_tag_rxni *)(mtag + 1);
+ rxni->ni = ni; /* We hold a reference. */
+ m_tag_prepend(m, mtag);
+#else
+ m->m_pkthdr.PH_loc.ptr = ni; /* We hold a reference. */
+#endif
}
LKPI_80211_LHW_RXQ_LOCK(lhw);
if (lhw->rxq_stopped) {
LKPI_80211_LHW_RXQ_UNLOCK(lhw);
m_freem(m);
+ counter_u64_add(ic->ic_ierrors, 1);
goto err;
}
- mbufq_enqueue(&lhw->rxq, m);
+ error = mbufq_enqueue(&lhw->rxq, m);
+ if (error != 0) {
+ LKPI_80211_LHW_RXQ_UNLOCK(lhw);
+ m_freem(m);
+ counter_u64_add(ic->ic_ierrors, 1);
+#ifdef LINUXKPI_DEBUG_80211
+ if (linuxkpi_debug_80211 & D80211_TRACE_RX)
+ ic_printf(ni->ni_ic, "%s: mbufq_enqueue failed: %d\n",
+ __func__, error);
+#endif
+ goto err;
+ }
taskqueue_enqueue(taskqueue_thread, &lhw->rxq_task);
LKPI_80211_LHW_RXQ_UNLOCK(lhw);
@@ -5173,18 +7113,158 @@ linuxkpi_ieee80211_get_tid(struct ieee80211_hdr *hdr, bool nonqos_ok)
return (tid);
}
+/* -------------------------------------------------------------------------- */
+
+static void
+lkpi_wiphy_work(struct work_struct *work)
+{
+ struct lkpi_wiphy *lwiphy;
+ struct wiphy *wiphy;
+ struct wiphy_work *wk;
+
+ lwiphy = container_of(work, struct lkpi_wiphy, wwk);
+ wiphy = LWIPHY_TO_WIPHY(lwiphy);
+
+ wiphy_lock(wiphy);
+
+ LKPI_80211_LWIPHY_WORK_LOCK(lwiphy);
+ wk = list_first_entry_or_null(&lwiphy->wwk_list, struct wiphy_work, entry);
+ /* If there is nothing we do nothing. */
+ if (wk == NULL) {
+ LKPI_80211_LWIPHY_WORK_UNLOCK(lwiphy);
+ wiphy_unlock(wiphy);
+ return;
+ }
+ list_del_init(&wk->entry);
+
+ /* More work to do? */
+ if (!list_empty(&lwiphy->wwk_list))
+ schedule_work(work);
+ LKPI_80211_LWIPHY_WORK_UNLOCK(lwiphy);
+
+ /* Finally call the (*wiphy_work_fn)() function. */
+ wk->fn(wiphy, wk);
+
+ wiphy_unlock(wiphy);
+}
+
+void
+linuxkpi_wiphy_work_queue(struct wiphy *wiphy, struct wiphy_work *wwk)
+{
+ struct lkpi_wiphy *lwiphy;
+
+ lwiphy = WIPHY_TO_LWIPHY(wiphy);
+
+ LKPI_80211_LWIPHY_WORK_LOCK(lwiphy);
+ /* Do not double-queue. */
+ if (list_empty(&wwk->entry))
+ list_add_tail(&wwk->entry, &lwiphy->wwk_list);
+ LKPI_80211_LWIPHY_WORK_UNLOCK(lwiphy);
+
+ /*
+ * See how ieee80211_queue_work() work continues in Linux or if things
+ * migrate here over time?
+ * Use a system queue from linux/workqueue.h for now.
+ */
+ queue_work(system_wq, &lwiphy->wwk);
+}
+
+void
+linuxkpi_wiphy_work_cancel(struct wiphy *wiphy, struct wiphy_work *wwk)
+{
+ struct lkpi_wiphy *lwiphy;
+
+ lwiphy = WIPHY_TO_LWIPHY(wiphy);
+
+ LKPI_80211_LWIPHY_WORK_LOCK(lwiphy);
+ /* Only cancel if queued. */
+ if (!list_empty(&wwk->entry))
+ list_del_init(&wwk->entry);
+ LKPI_80211_LWIPHY_WORK_UNLOCK(lwiphy);
+}
+
+void
+linuxkpi_wiphy_work_flush(struct wiphy *wiphy, struct wiphy_work *wwk)
+{
+ struct lkpi_wiphy *lwiphy;
+ struct wiphy_work *wk;
+
+ lwiphy = WIPHY_TO_LWIPHY(wiphy);
+ LKPI_80211_LWIPHY_WORK_LOCK(lwiphy);
+ /* If wwk is unset, flush everything; called when wiphy is shut down. */
+ if (wwk != NULL && list_empty(&wwk->entry)) {
+ LKPI_80211_LWIPHY_WORK_UNLOCK(lwiphy);
+ return;
+ }
+
+ while (!list_empty(&lwiphy->wwk_list)) {
+
+ wk = list_first_entry(&lwiphy->wwk_list, struct wiphy_work,
+ entry);
+ list_del_init(&wk->entry);
+ LKPI_80211_LWIPHY_WORK_UNLOCK(lwiphy);
+ wk->fn(wiphy, wk);
+ LKPI_80211_LWIPHY_WORK_LOCK(lwiphy);
+ if (wk == wwk)
+ break;
+ }
+ LKPI_80211_LWIPHY_WORK_UNLOCK(lwiphy);
+}
+
+void
+lkpi_wiphy_delayed_work_timer(struct timer_list *tl)
+{
+ struct wiphy_delayed_work *wdwk;
+
+ wdwk = from_timer(wdwk, tl, timer);
+ wiphy_work_queue(wdwk->wiphy, &wdwk->work);
+}
+
+void
+linuxkpi_wiphy_delayed_work_queue(struct wiphy *wiphy,
+ struct wiphy_delayed_work *wdwk, unsigned long delay)
+{
+ if (delay == 0) {
+ /* Run right away. */
+ del_timer(&wdwk->timer);
+ wiphy_work_queue(wiphy, &wdwk->work);
+ } else {
+ wdwk->wiphy = wiphy;
+ mod_timer(&wdwk->timer, jiffies + delay);
+ }
+}
+
+void
+linuxkpi_wiphy_delayed_work_cancel(struct wiphy *wiphy,
+ struct wiphy_delayed_work *wdwk)
+{
+ del_timer_sync(&wdwk->timer);
+ wiphy_work_cancel(wiphy, &wdwk->work);
+}
+
+/* -------------------------------------------------------------------------- */
+
struct wiphy *
linuxkpi_wiphy_new(const struct cfg80211_ops *ops, size_t priv_len)
{
struct lkpi_wiphy *lwiphy;
+ struct wiphy *wiphy;
lwiphy = kzalloc(sizeof(*lwiphy) + priv_len, GFP_KERNEL);
if (lwiphy == NULL)
return (NULL);
lwiphy->ops = ops;
- /* XXX TODO */
- return (LWIPHY_TO_WIPHY(lwiphy));
+ LKPI_80211_LWIPHY_WORK_LOCK_INIT(lwiphy);
+ INIT_LIST_HEAD(&lwiphy->wwk_list);
+ INIT_WORK(&lwiphy->wwk, lkpi_wiphy_work);
+
+ wiphy = LWIPHY_TO_WIPHY(lwiphy);
+
+ mutex_init(&wiphy->mtx);
+ TODO();
+
+ return (wiphy);
}
void
@@ -5195,10 +7275,45 @@ linuxkpi_wiphy_free(struct wiphy *wiphy)
if (wiphy == NULL)
return;
+ linuxkpi_wiphy_work_flush(wiphy, NULL);
+ mutex_destroy(&wiphy->mtx);
+
lwiphy = WIPHY_TO_LWIPHY(wiphy);
+ LKPI_80211_LWIPHY_WORK_LOCK_DESTROY(lwiphy);
+
kfree(lwiphy);
}
+static uint32_t
+lkpi_cfg80211_calculate_bitrate_ht(struct rate_info *rate)
+{
+ TODO("cfg80211_calculate_bitrate_ht");
+ return (rate->legacy);
+}
+
+static uint32_t
+lkpi_cfg80211_calculate_bitrate_vht(struct rate_info *rate)
+{
+ TODO("cfg80211_calculate_bitrate_vht");
+ return (rate->legacy);
+}
+
+uint32_t
+linuxkpi_cfg80211_calculate_bitrate(struct rate_info *rate)
+{
+
+ /* Beware: order! */
+ if (rate->flags & RATE_INFO_FLAGS_MCS)
+ return (lkpi_cfg80211_calculate_bitrate_ht(rate));
+
+ if (rate->flags & RATE_INFO_FLAGS_VHT_MCS)
+ return (lkpi_cfg80211_calculate_bitrate_vht(rate));
+
+ IMPROVE("HE/EHT/...");
+
+ return (rate->legacy);
+}
+
uint32_t
linuxkpi_ieee80211_channel_to_frequency(uint32_t channel,
enum nl80211_band band)
@@ -5232,14 +7347,14 @@ lkpi_find_lsta_by_ni(struct lkpi_vif *lvif, struct ieee80211_node *ni)
{
struct lkpi_sta *lsta, *temp;
- LKPI_80211_LVIF_LOCK(lvif);
- TAILQ_FOREACH_SAFE(lsta, &lvif->lsta_head, lsta_entry, temp) {
+ rcu_read_lock();
+ list_for_each_entry_rcu(lsta, &lvif->lsta_list, lsta_list) {
if (lsta->ni == ni) {
- LKPI_80211_LVIF_UNLOCK(lvif);
+ rcu_read_unlock();
return (lsta);
}
}
- LKPI_80211_LVIF_UNLOCK(lvif);
+ rcu_read_unlock();
return (NULL);
}
@@ -5249,20 +7364,20 @@ struct ieee80211_sta *
linuxkpi_ieee80211_find_sta(struct ieee80211_vif *vif, const u8 *peer)
{
struct lkpi_vif *lvif;
- struct lkpi_sta *lsta, *temp;
+ struct lkpi_sta *lsta;
struct ieee80211_sta *sta;
lvif = VIF_TO_LVIF(vif);
- LKPI_80211_LVIF_LOCK(lvif);
- TAILQ_FOREACH_SAFE(lsta, &lvif->lsta_head, lsta_entry, temp) {
+ rcu_read_lock();
+ list_for_each_entry_rcu(lsta, &lvif->lsta_list, lsta_list) {
sta = LSTA_TO_STA(lsta);
if (IEEE80211_ADDR_EQ(sta->addr, peer)) {
- LKPI_80211_LVIF_UNLOCK(lvif);
+ rcu_read_unlock();
return (sta);
}
}
- LKPI_80211_LVIF_UNLOCK(lvif);
+ rcu_read_unlock();
return (NULL);
}
@@ -5311,6 +7426,7 @@ linuxkpi_ieee80211_tx_dequeue(struct ieee80211_hw *hw,
struct lkpi_vif *lvif;
struct sk_buff *skb;
+ IMPROVE("wiphy_lock? or assert?");
skb = NULL;
ltxq = TXQ_TO_LTXQ(txq);
ltxq->seen_dequeue = true;
@@ -5425,12 +7541,6 @@ linuxkpi_ieee80211_tx_status_ext(struct ieee80211_hw *hw,
}
if (ni != NULL) {
- int ridx __unused;
-#ifdef LINUXKPI_DEBUG_80211
- int old_rate;
-
- old_rate = ni->ni_vap->iv_bss->ni_txrate;
-#endif
txs.pktlen = skb->len;
txs.flags |= IEEE80211_RATECTL_STATUS_PKTLEN;
if (info->status.rates[0].count > 1) {
@@ -5447,16 +7557,14 @@ linuxkpi_ieee80211_tx_status_ext(struct ieee80211_hw *hw,
txs.flags |= IEEE80211_RATECTL_STATUS_RSSI;
}
- IMPROVE("only update of rate matches but that requires us to get a proper rate");
+ IMPROVE("only update rate if needed but that requires us to get a proper rate from mo_sta_statistics");
ieee80211_ratectl_tx_complete(ni, &txs);
- ridx = ieee80211_ratectl_rate(ni->ni_vap->iv_bss, NULL, 0);
+ ieee80211_ratectl_rate(ni->ni_vap->iv_bss, NULL, 0);
#ifdef LINUXKPI_DEBUG_80211
if (linuxkpi_debug_80211 & D80211_TRACE_TX) {
- printf("TX-RATE: %s: old %d new %d ridx %d, "
- "long_retries %d\n", __func__,
- old_rate, ni->ni_vap->iv_bss->ni_txrate,
- ridx, txs.long_retries);
+ printf("TX-RATE: %s: long_retries %d\n", __func__,
+ txs.long_retries);
}
#endif
}
@@ -5802,8 +7910,8 @@ lkpi_ieee80211_wake_queues(struct ieee80211_hw *hw, int hwq)
#endif
lvif->hw_queue_stopped[ac] = false;
- LKPI_80211_LVIF_LOCK(lvif);
- TAILQ_FOREACH(lsta, &lvif->lsta_head, lsta_entry) {
+ rcu_read_lock();
+ list_for_each_entry_rcu(lsta, &lvif->lsta_list, lsta_list) {
struct ieee80211_sta *sta;
sta = LSTA_TO_STA(lsta);
@@ -5822,19 +7930,19 @@ lkpi_ieee80211_wake_queues(struct ieee80211_hw *hw, int hwq)
ltxq->stopped = false;
- /* XXX-BZ see when this explodes with all the locking. taskq? */
- lkpi_80211_mo_wake_tx_queue(hw, sta->txq[tid]);
+ if (!skb_queue_empty(&ltxq->skbq))
+ lkpi_80211_mo_wake_tx_queue(hw, sta->txq[tid]);
}
}
- LKPI_80211_LVIF_UNLOCK(lvif);
+ rcu_read_unlock();
}
}
}
LKPI_80211_LHW_LVIF_UNLOCK(lhw);
}
-void
-linuxkpi_ieee80211_wake_queues(struct ieee80211_hw *hw)
+static void
+lkpi_ieee80211_wake_queues_locked(struct ieee80211_hw *hw)
{
int i;
@@ -5844,13 +7952,23 @@ linuxkpi_ieee80211_wake_queues(struct ieee80211_hw *hw)
}
void
+linuxkpi_ieee80211_wake_queues(struct ieee80211_hw *hw)
+{
+ wiphy_lock(hw->wiphy);
+ lkpi_ieee80211_wake_queues_locked(hw);
+ wiphy_unlock(hw->wiphy);
+}
+
+void
linuxkpi_ieee80211_wake_queue(struct ieee80211_hw *hw, int qnum)
{
KASSERT(qnum < hw->queues, ("%s: qnum %d >= hw->queues %d, hw %p\n",
__func__, qnum, hw->queues, hw));
+ wiphy_lock(hw->wiphy);
lkpi_ieee80211_wake_queues(hw, qnum);
+ wiphy_unlock(hw->wiphy);
}
/* This is just hardware queues. */
@@ -5916,8 +8034,12 @@ void linuxkpi_ieee80211_schedule_txq(struct ieee80211_hw *hw,
if (!withoutpkts && ltxq_empty)
goto out;
- /* Make sure we do not double-schedule. */
- if (ltxq->txq_entry.tqe_next != NULL)
+ /*
+ * Make sure we do not double-schedule. We do this by checking tqe_prev,
+ * the previous entry in our tailq. tqe_prev is always valid if this entry
+ * is queued, tqe_next may be NULL if this is the only element in the list.
+ */
+ if (ltxq->txq_entry.tqe_prev != NULL)
goto out;
lhw = HW_TO_LHW(hw);
@@ -6128,6 +8250,43 @@ linuxkpi_cfg80211_bss_flush(struct wiphy *wiphy)
/* -------------------------------------------------------------------------- */
+/*
+ * hw->conf get initialized/set in various places for us:
+ * - linuxkpi_ieee80211_alloc_hw(): flags
+ * - linuxkpi_ieee80211_ifattach(): chandef
+ * - lkpi_ic_vap_create(): listen_interval
+ * - lkpi_ic_set_channel(): chandef, flags
+ */
+
+int lkpi_80211_update_chandef(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *new)
+{
+ struct cfg80211_chan_def *cd;
+ uint32_t changed;
+ int error;
+
+ changed = 0;
+ if (new == NULL || new->def.chan == NULL)
+ cd = NULL;
+ else
+ cd = &new->def;
+
+ if (cd && cd->chan != hw->conf.chandef.chan) {
+ /* 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");
+
+ if (changed == 0)
+ return (0);
+
+ error = lkpi_80211_mo_config(hw, changed);
+ return (error);
+}
+
+/* -------------------------------------------------------------------------- */
+
MODULE_VERSION(linuxkpi_wlan, 1);
MODULE_DEPEND(linuxkpi_wlan, linuxkpi, 1, 1, 1);
MODULE_DEPEND(linuxkpi_wlan, wlan, 1, 1, 1);
diff --git a/sys/compat/linuxkpi/common/src/linux_80211.h b/sys/compat/linuxkpi/common/src/linux_80211.h
index b0156a5ade3f..89afec1235bd 100644
--- a/sys/compat/linuxkpi/common/src/linux_80211.h
+++ b/sys/compat/linuxkpi/common/src/linux_80211.h
@@ -42,6 +42,12 @@
#ifndef _LKPI_SRC_LINUX_80211_H
#define _LKPI_SRC_LINUX_80211_H
+#include "opt_wlan.h"
+
+#if defined(IEEE80211_DEBUG) && !defined(LINUXKPI_DEBUG_80211)
+#define LINUXKPI_DEBUG_80211
+#endif
+
/* #define LINUXKPI_DEBUG_80211 */
#ifndef D80211_TODO
@@ -61,6 +67,7 @@
#define D80211_TRACEX (D80211_TRACE_TX|D80211_TRACE_RX)
#define D80211_TRACEX_DUMP (D80211_TRACE_TX_DUMP|D80211_TRACE_RX_DUMP)
#define D80211_TRACE_STA 0x00010000
+#define D80211_TRACE_HW_CRYPTO 0x00020000
#define D80211_TRACE_MO 0x00100000
#define D80211_TRACE_MODE 0x0f000000
#define D80211_TRACE_MODE_HT 0x01000000
@@ -72,12 +79,14 @@
if (linuxkpi_debug_80211 & D80211_IMPROVE_TXQ) \
printf("%s:%d: XXX LKPI80211 IMPROVE_TXQ\n", __func__, __LINE__)
-#define IMPROVE_HT(...) \
+#define IMPROVE_HT(fmt, ...) \
if (linuxkpi_debug_80211 & D80211_TRACE_MODE_HT) \
- printf("%s:%d: XXX LKPI80211 IMPROVE_HT\n", __func__, __LINE__)
+ printf("%s:%d: XXX LKPI80211 IMPROVE_HT " fmt "\n", \
+ __func__, __LINE__, ##__VA_ARGS__);
#define MTAG_ABI_LKPI80211 1707696513 /* LinuxKPI 802.11 KBI */
+#ifdef LKPI_80211_USE_MTAG
/*
* Deferred RX path.
* We need to pass *ni along (and possibly more in the future so
@@ -87,6 +96,7 @@
struct lkpi_80211_tag_rxni {
struct ieee80211_node *ni; /* MUST hold a reference to it. */
};
+#endif
struct lkpi_radiotap_tx_hdr {
struct ieee80211_radiotap_header wt_ihdr;
@@ -118,6 +128,8 @@ struct lkpi_radiotap_rx_hdr {
(1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | \
(1 << IEEE80211_RADIOTAP_DBM_ANTNOISE))
+struct lkpi_hw;
+
struct lkpi_txq {
TAILQ_ENTRY(lkpi_txq) txq_entry;
@@ -134,8 +146,9 @@ struct lkpi_txq {
struct lkpi_sta {
- TAILQ_ENTRY(lkpi_sta) lsta_entry;
+ struct list_head lsta_list;
struct ieee80211_node *ni;
+ struct ieee80211_hw *hw; /* back pointer f. locking. */
/* Deferred TX path. */
/* Eventually we might want to migrate this into net80211 entirely. */
@@ -144,21 +157,27 @@ struct lkpi_sta {
struct mbufq txq;
struct mtx txq_mtx;
- struct ieee80211_key_conf *kc;
+ struct ieee80211_key_conf *kc[IEEE80211_WEP_NKID];
enum ieee80211_sta_state state;
bool txq_ready; /* Can we run the taskq? */
bool added_to_drv; /* Driver knows; i.e. we called ...(). */
bool in_mgd; /* XXX-BZ should this be per-vif? */
+ struct station_info sinfo; /* statistics */
+
/* Must be last! */
struct ieee80211_sta sta __aligned(CACHE_LINE_SIZE);
};
#define STA_TO_LSTA(_sta) container_of(_sta, struct lkpi_sta, sta)
#define LSTA_TO_STA(_lsta) (&(_lsta)->sta)
+/* Either protected by wiphy lock or rcu for the list. */
struct lkpi_vif {
TAILQ_ENTRY(lkpi_vif) lvif_entry;
struct ieee80211vap iv_vap;
+ eventhandler_tag lvif_ifllevent;
+
+ struct sysctl_ctx_list sysctl_ctx;
struct mtx mtx;
struct wireless_dev wdev;
@@ -168,8 +187,13 @@ struct lkpi_vif {
enum ieee80211_state, int);
struct ieee80211_node * (*iv_update_bss)(struct ieee80211vap *,
struct ieee80211_node *);
- TAILQ_HEAD(, lkpi_sta) lsta_head;
+ struct list_head lsta_list;
+
struct lkpi_sta *lvif_bss;
+
+ struct ieee80211_node *key_update_iv_bss;
+ int ic_unlocked; /* Count of ic unlocks pending (*mo_set_key) */
+ int nt_unlocked; /* Count of nt unlocks pending (*mo_set_key) */
bool lvif_bss_synched;
bool added_to_drv; /* Driver knows; i.e. we called add_interface(). */
@@ -198,7 +222,7 @@ struct lkpi_hw { /* name it mac80211_sc? */
TAILQ_HEAD(, lkpi_vif) lvif_head;
struct sx lvif_sx;
- struct sx sx;
+ struct list_head lchanctx_list;
struct mtx txq_mtx;
uint32_t txq_generation[IEEE80211_NUM_ACS];
@@ -266,35 +290,42 @@ struct lkpi_hw { /* name it mac80211_sc? */
#define HW_TO_LHW(_hw) container_of(_hw, struct lkpi_hw, hw)
struct lkpi_chanctx {
+ struct list_head entry;
+
bool added_to_drv; /* Managed by MO */
- struct ieee80211_chanctx_conf conf __aligned(CACHE_LINE_SIZE);
+
+ struct ieee80211_chanctx_conf chanctx_conf __aligned(CACHE_LINE_SIZE);
};
#define LCHANCTX_TO_CHANCTX_CONF(_lchanctx) \
- (&(_lchanctx)->conf)
+ (&(_lchanctx)->chanctx_conf)
#define CHANCTX_CONF_TO_LCHANCTX(_conf) \
- container_of(_conf, struct lkpi_chanctx, conf)
+ container_of(_conf, struct lkpi_chanctx, chanctx_conf)
struct lkpi_wiphy {
const struct cfg80211_ops *ops;
+ struct work_struct wwk;
+ struct list_head wwk_list;
+ struct mtx wwk_mtx;
+
/* Must be last! */
struct wiphy wiphy __aligned(CACHE_LINE_SIZE);
};
#define WIPHY_TO_LWIPHY(_wiphy) container_of(_wiphy, struct lkpi_wiphy, wiphy)
#define LWIPHY_TO_WIPHY(_lwiphy) (&(_lwiphy)->wiphy)
-#define LKPI_80211_LHW_LOCK_INIT(_lhw) \
- sx_init_flags(&(_lhw)->sx, "lhw", SX_RECURSE);
-#define LKPI_80211_LHW_LOCK_DESTROY(_lhw) \
- sx_destroy(&(_lhw)->sx);
-#define LKPI_80211_LHW_LOCK(_lhw) \
- sx_xlock(&(_lhw)->sx)
-#define LKPI_80211_LHW_UNLOCK(_lhw) \
- sx_xunlock(&(_lhw)->sx)
-#define LKPI_80211_LHW_LOCK_ASSERT(_lhw) \
- sx_assert(&(_lhw)->sx, SA_LOCKED)
-#define LKPI_80211_LHW_UNLOCK_ASSERT(_lhw) \
- sx_assert(&(_lhw)->sx, SA_UNLOCKED)
+#define LKPI_80211_LWIPHY_WORK_LOCK_INIT(_lwiphy) \
+ mtx_init(&(_lwiphy)->wwk_mtx, "lwiphy-work", NULL, MTX_DEF);
+#define LKPI_80211_LWIPHY_WORK_LOCK_DESTROY(_lwiphy) \
+ mtx_destroy(&(_lwiphy)->wwk_mtx)
+#define LKPI_80211_LWIPHY_WORK_LOCK(_lwiphy) \
+ mtx_lock(&(_lwiphy)->wwk_mtx)
+#define LKPI_80211_LWIPHY_WORK_UNLOCK(_lwiphy) \
+ mtx_unlock(&(_lwiphy)->wwk_mtx)
+#define LKPI_80211_LWIPHY_WORK_LOCK_ASSERT(_lwiphy) \
+ mtx_assert(&(_lwiphy)->wwk_mtx, MA_OWNED)
+#define LKPI_80211_LWIPHY_WORK_UNLOCK_ASSERT(_lwiphy) \
+ mtx_assert(&(_lwiphy)->wwk_mtx, MA_NOTOWNED)
#define LKPI_80211_LHW_SCAN_LOCK_INIT(_lhw) \
mtx_init(&(_lhw)->scan_mtx, "lhw-scan", NULL, MTX_DEF | MTX_RECURSE);
@@ -368,7 +399,7 @@ struct lkpi_wiphy {
mtx_assert(&(_ltxq)->ltxq_mtx, MA_NOTOWNED)
int lkpi_80211_mo_start(struct ieee80211_hw *);
-void lkpi_80211_mo_stop(struct ieee80211_hw *);
+void lkpi_80211_mo_stop(struct ieee80211_hw *, bool);
int lkpi_80211_mo_get_antenna(struct ieee80211_hw *, u32 *, u32 *);
int lkpi_80211_mo_set_frag_threshold(struct ieee80211_hw *, uint32_t);
int lkpi_80211_mo_set_rts_threshold(struct ieee80211_hw *, uint32_t);
@@ -390,7 +421,7 @@ int lkpi_80211_mo_config(struct ieee80211_hw *, uint32_t);
int lkpi_80211_mo_assign_vif_chanctx(struct ieee80211_hw *, struct ieee80211_vif *,
struct ieee80211_bss_conf *, struct ieee80211_chanctx_conf *);
void lkpi_80211_mo_unassign_vif_chanctx(struct ieee80211_hw *, struct ieee80211_vif *,
- struct ieee80211_bss_conf *, struct ieee80211_chanctx_conf **);
+ struct ieee80211_bss_conf *, struct ieee80211_chanctx_conf *);
int lkpi_80211_mo_add_chanctx(struct ieee80211_hw *, struct ieee80211_chanctx_conf *);
void lkpi_80211_mo_change_chanctx(struct ieee80211_hw *,
struct ieee80211_chanctx_conf *, uint32_t);
@@ -417,6 +448,7 @@ int lkpi_80211_mo_set_key(struct ieee80211_hw *, enum set_key_cmd,
struct ieee80211_key_conf *);
int lkpi_80211_mo_ampdu_action(struct ieee80211_hw *, struct ieee80211_vif *,
struct ieee80211_ampdu_params *);
-
+int lkpi_80211_mo_sta_statistics(struct ieee80211_hw *, struct ieee80211_vif *,
+ struct ieee80211_sta *, struct station_info *);
#endif /* _LKPI_SRC_LINUX_80211_H */
diff --git a/sys/compat/linuxkpi/common/src/linux_80211_macops.c b/sys/compat/linuxkpi/common/src/linux_80211_macops.c
index 8cc885c037e3..78b2120f2d8c 100644
--- a/sys/compat/linuxkpi/common/src/linux_80211_macops.c
+++ b/sys/compat/linuxkpi/common/src/linux_80211_macops.c
@@ -40,9 +40,9 @@
#ifdef LINUXKPI_DEBUG_80211
#define LKPI_80211_TRACE_MO(fmt, ...) \
if (linuxkpi_debug_80211 & D80211_TRACE_MO) \
- printf("LKPI_80211_TRACE_MO %s:%d: %d %d %u_" fmt "\n", \
+ printf("LKPI_80211_TRACE_MO %s:%d: %d %d %lu: " fmt "\n", \
__func__, __LINE__, curcpu, curthread->td_tid, \
- (unsigned int)ticks, __VA_ARGS__)
+ jiffies, __VA_ARGS__)
#else
#define LKPI_80211_TRACE_MO(...) do { } while(0)
#endif
@@ -74,7 +74,7 @@ out:
}
void
-lkpi_80211_mo_stop(struct ieee80211_hw *hw)
+lkpi_80211_mo_stop(struct ieee80211_hw *hw, bool suspend)
{
struct lkpi_hw *lhw;
@@ -82,8 +82,8 @@ lkpi_80211_mo_stop(struct ieee80211_hw *hw)
if (lhw->ops->stop == NULL)
return;
- LKPI_80211_TRACE_MO("hw %p", hw);
- lhw->ops->stop(hw);
+ LKPI_80211_TRACE_MO("hw %p suspend %d", hw, suspend);
+ lhw->ops->stop(hw, suspend);
lhw->sc_flags &= ~LKPI_MAC80211_DRV_STARTED;
}
@@ -458,7 +458,7 @@ lkpi_80211_mo_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *
hw, vif, conf, chanctx_conf);
error = lhw->ops->assign_vif_chanctx(hw, vif, conf, chanctx_conf);
if (error == 0)
- vif->chanctx_conf = chanctx_conf;
+ vif->bss_conf.chanctx_conf = chanctx_conf;
out:
return (error);
@@ -466,21 +466,23 @@ out:
void
lkpi_80211_mo_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- struct ieee80211_bss_conf *conf, struct ieee80211_chanctx_conf **chanctx_conf)
+ struct ieee80211_bss_conf *conf, struct ieee80211_chanctx_conf *chanctx_conf)
{
struct lkpi_hw *lhw;
+ might_sleep();
+ lockdep_assert_wiphy(hw->wiphy);
+
lhw = HW_TO_LHW(hw);
if (lhw->ops->unassign_vif_chanctx == NULL)
return;
- if (*chanctx_conf == NULL)
+ if (chanctx_conf == NULL)
return;
LKPI_80211_TRACE_MO("hw %p vif %p bss_conf %p chanctx_conf %p",
- hw, vif, conf, *chanctx_conf);
- lhw->ops->unassign_vif_chanctx(hw, vif, conf, *chanctx_conf);
- *chanctx_conf = NULL;
+ hw, vif, conf, chanctx_conf);
+ lhw->ops->unassign_vif_chanctx(hw, vif, conf, chanctx_conf);
}
@@ -551,6 +553,9 @@ lkpi_80211_mo_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vi
lhw->ops->bss_info_changed == NULL)
return;
+ if (changed == 0)
+ return;
+
LKPI_80211_TRACE_MO("hw %p vif %p conf %p changed %#jx", hw, vif, conf, (uintmax_t)changed);
if (lhw->ops->link_info_changed != NULL)
lhw->ops->link_info_changed(hw, vif, conf, changed);
@@ -683,6 +688,8 @@ lkpi_80211_mo_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
struct lkpi_hw *lhw;
int error;
+ lockdep_assert_wiphy(hw->wiphy);
+
lhw = HW_TO_LHW(hw);
if (lhw->ops->set_key == NULL) {
error = EOPNOTSUPP;
@@ -717,3 +724,33 @@ lkpi_80211_mo_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
out:
return (error);
}
+
+int
+lkpi_80211_mo_sta_statistics(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, struct station_info *sinfo)
+{
+ struct lkpi_hw *lhw;
+ struct lkpi_sta *lsta;
+ int error;
+
+ lhw = HW_TO_LHW(hw);
+ if (lhw->ops->sta_statistics == NULL) {
+ error = EOPNOTSUPP;
+ goto out;
+ }
+
+ lsta = STA_TO_LSTA(sta);
+ if (!lsta->added_to_drv) {
+ error = EEXIST;
+ goto out;
+ }
+
+ lockdep_assert_wiphy(hw->wiphy);
+
+ LKPI_80211_TRACE_MO("hw %p vif %p sta %p sinfo %p", hw, vif, sta, sinfo);
+ lhw->ops->sta_statistics(hw, vif, sta, sinfo);
+ error = 0;
+
+out:
+ return (error);
+}
diff --git a/sys/compat/linuxkpi/common/src/linux_acpi.c b/sys/compat/linuxkpi/common/src/linux_acpi.c
index 60ec838e9da7..d18c69d9210d 100644
--- a/sys/compat/linuxkpi/common/src/linux_acpi.c
+++ b/sys/compat/linuxkpi/common/src/linux_acpi.c
@@ -39,6 +39,7 @@
#include <linux/notifier.h>
#include <linux/suspend.h>
+#include <linux/uuid.h>
#include <acpi/acpi_bus.h>
#include <acpi/video.h>
@@ -99,6 +100,17 @@ acpi_evaluate_dsm_typed(ACPI_HANDLE handle, const char *uuid, int rev,
argv4, &buf, type)) ? (ACPI_OBJECT *)buf.Pointer : NULL);
}
+union linuxkpi_acpi_object *
+acpi_evaluate_dsm(ACPI_HANDLE ObjHandle, const guid_t *guid,
+ UINT64 rev, UINT64 func, union linuxkpi_acpi_object *pkg)
+{
+ ACPI_BUFFER buf;
+
+ return (ACPI_SUCCESS(acpi_EvaluateDSM(ObjHandle, (const uint8_t *)guid,
+ rev, func, (ACPI_OBJECT *)pkg, &buf)) ?
+ (union linuxkpi_acpi_object *)buf.Pointer : NULL);
+}
+
static void
linux_handle_power_suspend_event(void *arg __unused)
{
@@ -180,6 +192,7 @@ struct acpi_dev_present_ctx {
const char *hid;
const char *uid;
int64_t hrv;
+ struct acpi_device *dev;
};
static ACPI_STATUS
@@ -187,6 +200,7 @@ acpi_dev_present_cb(ACPI_HANDLE handle, UINT32 level, void *context,
void **result)
{
ACPI_DEVICE_INFO *devinfo;
+ struct acpi_device *dev;
struct acpi_dev_present_ctx *match = context;
bool present = false;
UINT32 sta, hrv;
@@ -230,6 +244,11 @@ acpi_dev_present_cb(ACPI_HANDLE handle, UINT32 level, void *context,
return (AE_OK);
}
+ dev = acpi_get_device(handle);
+ if (dev == NULL)
+ return (AE_OK);
+ match->dev = dev;
+
return (AE_ERROR);
}
@@ -249,6 +268,24 @@ lkpi_acpi_dev_present(const char *hid, const char *uid, int64_t hrv)
return (rv == AE_ERROR);
}
+struct acpi_device *
+lkpi_acpi_dev_get_first_match_dev(const char *hid, const char *uid,
+ int64_t hrv)
+{
+ struct acpi_dev_present_ctx match;
+ int rv;
+
+ match.hid = hid;
+ match.uid = uid;
+ match.hrv = hrv;
+ match.dev = NULL;
+
+ rv = AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX, acpi_dev_present_cb, NULL, &match, NULL);
+
+ return (rv == AE_ERROR ? match.dev : NULL);
+}
+
static void
linux_register_acpi_event_handlers(void *arg __unused)
{
@@ -298,6 +335,13 @@ acpi_evaluate_dsm_typed(ACPI_HANDLE handle, const char *uuid, int rev,
return (NULL);
}
+union linuxkpi_acpi_object *
+acpi_evaluate_dsm(ACPI_HANDLE ObjHandle, const guid_t *guid,
+ UINT64 rev, UINT64 func, union linuxkpi_acpi_object *pkg)
+{
+ return (NULL);
+}
+
int
register_acpi_notifier(struct notifier_block *nb)
{
@@ -322,4 +366,11 @@ lkpi_acpi_dev_present(const char *hid, const char *uid, int64_t hrv)
return (false);
}
+struct acpi_device *
+lkpi_acpi_dev_get_first_match_dev(const char *hid, const char *uid,
+ int64_t hrv)
+{
+ return (NULL);
+}
+
#endif /* !DEV_ACPI */
diff --git a/sys/compat/linuxkpi/common/src/linux_aperture.c b/sys/compat/linuxkpi/common/src/linux_aperture.c
index 15a56839fa9c..21c7041fc851 100644
--- a/sys/compat/linuxkpi/common/src/linux_aperture.c
+++ b/sys/compat/linuxkpi/common/src/linux_aperture.c
@@ -20,7 +20,7 @@
* driver can be active at any given time. Many systems load a generic
* graphics drivers, such as EFI-GOP or VESA, early during the boot process.
* During later boot stages, they replace the generic driver with a dedicated,
- * hardware-specific driver. To take over the device the dedicated driver
+ * hardware-specific driver. To take over the device, the dedicated driver
* first has to remove the generic driver. Aperture functions manage
* ownership of framebuffer memory and hand-over between drivers.
*
@@ -43,7 +43,7 @@
* base = mem->start;
* size = resource_size(mem);
*
- * ret = aperture_remove_conflicting_devices(base, size, false, "example");
+ * ret = aperture_remove_conflicting_devices(base, size, "example");
* if (ret)
* return ret;
*
@@ -76,7 +76,7 @@
* generic EFI or VESA drivers, have to register themselves as owners of their
* framebuffer apertures. Ownership of the framebuffer memory is achieved
* by calling devm_aperture_acquire_for_platform_device(). If successful, the
- * driveris the owner of the framebuffer range. The function fails if the
+ * driver is the owner of the framebuffer range. The function fails if the
* framebuffer is already owned by another driver. See below for an example.
*
* .. code-block:: c
@@ -126,7 +126,7 @@
* et al for the registered framebuffer range, the aperture helpers call
* platform_device_unregister() and the generic driver unloads itself. The
* generic driver also has to provide a remove function to make this work.
- * Once hot unplugged fro mhardware, it may not access the device's
+ * Once hot unplugged from hardware, it may not access the device's
* registers, framebuffer memory, ROM, etc afterwards.
*/
@@ -203,7 +203,7 @@ static void aperture_detach_platform_device(struct device *dev)
/*
* Remove the device from the device hierarchy. This is the right thing
- * to do for firmware-based DRM drivers, such as EFI, VESA or VGA. After
+ * to do for firmware-based fb drivers, such as EFI, VESA or VGA. After
* the new driver takes over the hardware, the firmware device's state
* will be lost.
*
@@ -274,7 +274,6 @@ static void aperture_detach_devices(resource_size_t base, resource_size_t size)
* aperture_remove_conflicting_devices - remove devices in the given range
* @base: the aperture's base address in physical memory
* @size: aperture size in bytes
- * @primary: also kick vga16fb if present; only relevant for VGA devices
* @name: a descriptive name of the requesting driver
*
* This function removes devices that own apertures within @base and @size.
@@ -283,7 +282,7 @@ static void aperture_detach_devices(resource_size_t base, resource_size_t size)
* 0 on success, or a negative errno code otherwise
*/
int aperture_remove_conflicting_devices(resource_size_t base, resource_size_t size,
- bool primary, const char *name)
+ const char *name)
{
/*
* If a driver asked to unregister a platform device registered by
@@ -300,19 +299,46 @@ int aperture_remove_conflicting_devices(resource_size_t base, resource_size_t si
aperture_detach_devices(base, size);
- /*
- * If this is the primary adapter, there could be a VGA device
- * that consumes the VGA framebuffer I/O range. Remove this device
- * as well.
- */
- if (primary)
- aperture_detach_devices(VGA_FB_PHYS_BASE, VGA_FB_PHYS_SIZE);
-
return 0;
}
EXPORT_SYMBOL(aperture_remove_conflicting_devices);
/**
+ * __aperture_remove_legacy_vga_devices - remove legacy VGA devices of a PCI devices
+ * @pdev: PCI device
+ *
+ * This function removes VGA devices provided by @pdev, such as a VGA
+ * framebuffer or a console. This is useful if you have a VGA-compatible
+ * PCI graphics device with framebuffers in non-BAR locations. Drivers
+ * should acquire ownership of those memory areas and afterwards call
+ * this helper to release remaining VGA devices.
+ *
+ * If your hardware has its framebuffers accessible via PCI BARS, use
+ * aperture_remove_conflicting_pci_devices() instead. The function will
+ * release any VGA devices automatically.
+ *
+ * WARNING: Apparently we must remove graphics drivers before calling
+ * this helper. Otherwise the vga fbdev driver falls over if
+ * we have vgacon configured.
+ *
+ * Returns:
+ * 0 on success, or a negative errno code otherwise
+ */
+int __aperture_remove_legacy_vga_devices(struct pci_dev *pdev)
+{
+ /* VGA framebuffer */
+ aperture_detach_devices(VGA_FB_PHYS_BASE, VGA_FB_PHYS_SIZE);
+
+ /* VGA textmode console */
+#ifdef __linux__
+ return vga_remove_vgacon(pdev);
+#elif defined(__FreeBSD__)
+ return 0;
+#endif
+}
+EXPORT_SYMBOL(__aperture_remove_legacy_vga_devices);
+
+/**
* aperture_remove_conflicting_pci_devices - remove existing framebuffers for PCI devices
* @pdev: PCI device
* @name: a descriptive name of the requesting driver
@@ -328,14 +354,14 @@ int aperture_remove_conflicting_pci_devices(struct pci_dev *pdev, const char *na
{
bool primary = false;
resource_size_t base, size;
- int bar, ret;
+ int bar, ret = 0;
-#ifdef CONFIG_X86
#ifdef __linux__
- primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW;
-#elif defined(__FreeBSD__)
- primary = NULL;
-#endif
+ if (pdev == vga_default_device())
+ primary = true;
+
+ if (primary)
+ sysfb_disable();
#endif
for (bar = 0; bar < PCI_STD_NUM_BARS; ++bar) {
@@ -344,22 +370,18 @@ int aperture_remove_conflicting_pci_devices(struct pci_dev *pdev, const char *na
base = pci_resource_start(pdev, bar);
size = pci_resource_len(pdev, bar);
- ret = aperture_remove_conflicting_devices(base, size, primary, name);
- if (ret)
- return ret;
+ aperture_detach_devices(base, size);
}
/*
- * WARNING: Apparently we must kick fbdev drivers before vgacon,
- * otherwise the vga fbdev driver falls over.
+ * If this is the primary adapter, there could be a VGA device
+ * that consumes the VGA framebuffer I/O range. Remove this
+ * device as well.
*/
-#ifdef __linux__
- ret = vga_remove_vgacon(pdev);
- if (ret)
- return ret;
-#endif
+ if (primary)
+ ret = __aperture_remove_legacy_vga_devices(pdev);
- return 0;
+ return ret;
}
EXPORT_SYMBOL(aperture_remove_conflicting_pci_devices);
diff --git a/sys/compat/linuxkpi/common/src/linux_cmdline.c b/sys/compat/linuxkpi/common/src/linux_cmdline.c
new file mode 100644
index 000000000000..0cfa1d56ee6a
--- /dev/null
+++ b/sys/compat/linuxkpi/common/src/linux_cmdline.c
@@ -0,0 +1,63 @@
+/*-
+ * Copyright (c) 2022 Beckhoff Automation GmbH & Co. KG
+ *
+ * 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/systm.h>
+
+#include <video/cmdline.h>
+
+const char *
+video_get_options(const char *connector_name)
+{
+ char tunable[64];
+ const char *options;
+
+ /*
+ * A user may use loader tunables to set a specific mode for the
+ * console. Tunables are read in the following order:
+ * 1. kern.vt.fb.modes.$connector_name
+ * 2. kern.vt.fb.default_mode
+ *
+ * Example of a mode specific to the LVDS connector:
+ * kern.vt.fb.modes.LVDS="1024x768"
+ *
+ * Example of a mode applied to all connectors not having a
+ * connector-specific mode:
+ * kern.vt.fb.default_mode="640x480"
+ */
+ snprintf(tunable, sizeof(tunable), "kern.vt.fb.modes.%s",
+ connector_name);
+ if (bootverbose) {
+ printf("[drm] Connector %s: get mode from tunables:\n", connector_name);
+ printf("[drm] - %s\n", tunable);
+ printf("[drm] - kern.vt.fb.default_mode\n");
+ }
+ options = kern_getenv(tunable);
+ if (options == NULL)
+ options = kern_getenv("kern.vt.fb.default_mode");
+
+ return (options);
+}
diff --git a/sys/compat/linuxkpi/common/src/linux_compat.c b/sys/compat/linuxkpi/common/src/linux_compat.c
index a6eb7bb17e16..dcdec0dfcc78 100644
--- a/sys/compat/linuxkpi/common/src/linux_compat.c
+++ b/sys/compat/linuxkpi/common/src/linux_compat.c
@@ -50,6 +50,7 @@
#include <sys/rwlock.h>
#include <sys/mman.h>
#include <sys/stack.h>
+#include <sys/stdarg.h>
#include <sys/sysent.h>
#include <sys/time.h>
#include <sys/user.h>
@@ -59,8 +60,7 @@
#include <vm/vm_object.h>
#include <vm/vm_page.h>
#include <vm/vm_pager.h>
-
-#include <machine/stdarg.h>
+#include <vm/vm_radix.h>
#if defined(__i386__) || defined(__amd64__)
#include <machine/cputypes.h>
@@ -75,6 +75,7 @@
#include <linux/moduleparam.h>
#include <linux/cdev.h>
#include <linux/file.h>
+#include <linux/fs.h>
#include <linux/sysfs.h>
#include <linux/mm.h>
#include <linux/io.h>
@@ -95,6 +96,8 @@
#include <linux/rcupdate.h>
#include <linux/interval_tree.h>
#include <linux/interval_tree_generic.h>
+#include <linux/printk.h>
+#include <linux/seq_file.h>
#if defined(__i386__) || defined(__amd64__)
#include <asm/smp.h>
@@ -117,6 +120,10 @@ int linuxkpi_debug;
SYSCTL_INT(_compat_linuxkpi, OID_AUTO, debug, CTLFLAG_RWTUN,
&linuxkpi_debug, 0, "Set to enable pr_debug() prints. Clear to disable.");
+int linuxkpi_rcu_debug;
+SYSCTL_INT(_compat_linuxkpi, OID_AUTO, rcu_debug, CTLFLAG_RWTUN,
+ &linuxkpi_rcu_debug, 0, "Set to enable RCU warning. Clear to disable.");
+
int linuxkpi_warn_dump_stack = 0;
SYSCTL_INT(_compat_linuxkpi, OID_AUTO, warn_dump_stack, CTLFLAG_RWTUN,
&linuxkpi_warn_dump_stack, 0,
@@ -342,13 +349,12 @@ error:
}
struct class *
-class_create(struct module *owner, const char *name)
+lkpi_class_create(const char *name)
{
struct class *class;
int error;
class = kzalloc(sizeof(*class), M_WAITOK);
- class->owner = owner;
class->name = name;
class->class_release = linux_class_kfree;
error = class_register(class);
@@ -382,9 +388,9 @@ linux_kq_assert_lock(void *arg, int what)
spinlock_t *s = arg;
if (what == LA_LOCKED)
- mtx_assert(&s->m, MA_OWNED);
+ mtx_assert(s, MA_OWNED);
else
- mtx_assert(&s->m, MA_NOTOWNED);
+ mtx_assert(s, MA_NOTOWNED);
#endif
}
@@ -641,6 +647,7 @@ int
zap_vma_ptes(struct vm_area_struct *vma, unsigned long address,
unsigned long size)
{
+ struct pctrie_iter pages;
vm_object_t obj;
vm_page_t m;
@@ -648,9 +655,8 @@ zap_vma_ptes(struct vm_area_struct *vma, unsigned long address,
if (obj == NULL || (obj->flags & OBJ_UNMANAGED) != 0)
return (-ENOTSUP);
VM_OBJECT_RLOCK(obj);
- for (m = vm_page_find_least(obj, OFF_TO_IDX(address));
- m != NULL && m->pindex < OFF_TO_IDX(address + size);
- m = TAILQ_NEXT(m, listq))
+ vm_page_iter_limit_init(&pages, obj, OFF_TO_IDX(address + size));
+ VM_RADIX_FOREACH_FROM(m, &pages, OFF_TO_IDX(address))
pmap_remove_all(m);
VM_OBJECT_RUNLOCK(obj);
return (0);
@@ -769,7 +775,7 @@ linux_dev_fdopen(struct cdev *dev, int fflags, struct thread *td,
}
/* hold on to the vnode - used for fstat() */
- vhold(filp->f_vnode);
+ vref(filp->f_vnode);
/* release the file from devfs */
finit(file, filp->f_mode, DTYPE_DEV, filp, &linuxfileops);
@@ -1079,6 +1085,58 @@ linux_poll_wakeup(struct linux_file *filp)
spin_unlock(&filp->f_kqlock);
}
+static struct linux_file *
+__get_file_rcu(struct linux_file **f)
+{
+ struct linux_file *file1, *file2;
+
+ file1 = READ_ONCE(*f);
+ if (file1 == NULL)
+ return (NULL);
+
+ if (!refcount_acquire_if_not_zero(
+ file1->_file == NULL ? &file1->f_count : &file1->_file->f_count))
+ return (ERR_PTR(-EAGAIN));
+
+ file2 = READ_ONCE(*f);
+ if (file2 == file1)
+ return (file2);
+
+ fput(file1);
+ return (ERR_PTR(-EAGAIN));
+}
+
+struct linux_file *
+linux_get_file_rcu(struct linux_file **f)
+{
+ struct linux_file *file1;
+
+ for (;;) {
+ file1 = __get_file_rcu(f);
+ if (file1 == NULL)
+ return (NULL);
+
+ if (IS_ERR(file1))
+ continue;
+
+ return (file1);
+ }
+}
+
+struct linux_file *
+get_file_active(struct linux_file **f)
+{
+ struct linux_file *file1;
+
+ rcu_read_lock();
+ file1 = __get_file_rcu(f);
+ rcu_read_unlock();
+ if (IS_ERR(file1))
+ file1 = NULL;
+
+ return (file1);
+}
+
static void
linux_file_kqfilter_detach(struct knote *kn)
{
@@ -1094,7 +1152,7 @@ linux_file_kqfilter_read_event(struct knote *kn, long hint)
{
struct linux_file *filp = kn->kn_hook;
- mtx_assert(&filp->f_kqlock.m, MA_OWNED);
+ mtx_assert(&filp->f_kqlock, MA_OWNED);
return ((filp->f_kqflags & LINUX_KQ_FLAG_NEED_READ) ? 1 : 0);
}
@@ -1104,18 +1162,18 @@ linux_file_kqfilter_write_event(struct knote *kn, long hint)
{
struct linux_file *filp = kn->kn_hook;
- mtx_assert(&filp->f_kqlock.m, MA_OWNED);
+ mtx_assert(&filp->f_kqlock, MA_OWNED);
return ((filp->f_kqflags & LINUX_KQ_FLAG_NEED_WRITE) ? 1 : 0);
}
-static struct filterops linux_dev_kqfiltops_read = {
+static const struct filterops linux_dev_kqfiltops_read = {
.f_isfd = 1,
.f_detach = linux_file_kqfilter_detach,
.f_event = linux_file_kqfilter_read_event,
};
-static struct filterops linux_dev_kqfiltops_write = {
+static const struct filterops linux_dev_kqfiltops_write = {
.f_isfd = 1,
.f_detach = linux_file_kqfilter_detach,
.f_event = linux_file_kqfilter_write_event,
@@ -1501,7 +1559,7 @@ linux_file_close(struct file *file, struct thread *td)
error = -OPW(file, td, release(filp->f_vnode, filp));
funsetown(&filp->f_sigio);
if (filp->f_vnode != NULL)
- vdrop(filp->f_vnode);
+ vrele(filp->f_vnode);
linux_drop_fop(ldev);
ldev = filp->f_cdev;
if (ldev != NULL)
@@ -1734,7 +1792,7 @@ linux_file_kcmp(struct file *fp1, struct file *fp2, struct thread *td)
return (kcmp_cmp((uintptr_t)filp1->f_cdev, (uintptr_t)filp2->f_cdev));
}
-struct fileops linuxfileops = {
+const struct fileops linuxfileops = {
.fo_read = linux_file_read,
.fo_write = linux_file_write,
.fo_truncate = invfo_truncate,
@@ -1913,6 +1971,84 @@ kasprintf(gfp_t gfp, const char *fmt, ...)
return (p);
}
+int
+__lkpi_hexdump_printf(void *arg1 __unused, const char *fmt, ...)
+{
+ va_list ap;
+ int result;
+
+ va_start(ap, fmt);
+ result = vprintf(fmt, ap);
+ va_end(ap);
+ return (result);
+}
+
+int
+__lkpi_hexdump_sbuf_printf(void *arg1, const char *fmt, ...)
+{
+ va_list ap;
+ int result;
+
+ va_start(ap, fmt);
+ result = sbuf_vprintf(arg1, fmt, ap);
+ va_end(ap);
+ return (result);
+}
+
+void
+lkpi_hex_dump(int(*_fpf)(void *, const char *, ...), void *arg1,
+ const char *level, const char *prefix_str,
+ const int prefix_type, const int rowsize, const int groupsize,
+ const void *buf, size_t len, const bool ascii)
+{
+ typedef const struct { long long value; } __packed *print_64p_t;
+ typedef const struct { uint32_t value; } __packed *print_32p_t;
+ typedef const struct { uint16_t value; } __packed *print_16p_t;
+ const void *buf_old = buf;
+ int row;
+
+ while (len > 0) {
+ if (level != NULL)
+ _fpf(arg1, "%s", level);
+ if (prefix_str != NULL)
+ _fpf(arg1, "%s ", prefix_str);
+
+ switch (prefix_type) {
+ case DUMP_PREFIX_ADDRESS:
+ _fpf(arg1, "[%p] ", buf);
+ break;
+ case DUMP_PREFIX_OFFSET:
+ _fpf(arg1, "[%#tx] ", ((const char *)buf -
+ (const char *)buf_old));
+ break;
+ default:
+ break;
+ }
+ for (row = 0; row != rowsize; row++) {
+ if (groupsize == 8 && len > 7) {
+ _fpf(arg1, "%016llx ", ((print_64p_t)buf)->value);
+ buf = (const uint8_t *)buf + 8;
+ len -= 8;
+ } else if (groupsize == 4 && len > 3) {
+ _fpf(arg1, "%08x ", ((print_32p_t)buf)->value);
+ buf = (const uint8_t *)buf + 4;
+ len -= 4;
+ } else if (groupsize == 2 && len > 1) {
+ _fpf(arg1, "%04x ", ((print_16p_t)buf)->value);
+ buf = (const uint8_t *)buf + 2;
+ len -= 2;
+ } else if (len > 0) {
+ _fpf(arg1, "%02x ", *(const uint8_t *)buf);
+ buf = (const uint8_t *)buf + 1;
+ len--;
+ } else {
+ break;
+ }
+ }
+ _fpf(arg1, "\n");
+ }
+}
+
static void
linux_timer_callback_wrapper(void *context)
{
@@ -1934,8 +2070,24 @@ linux_timer_callback_wrapper(void *context)
timer->function(timer->data);
}
+static int
+linux_timer_jiffies_until(unsigned long expires)
+{
+ unsigned long delta = expires - jiffies;
+
+ /*
+ * Guard against already expired values and make sure that the value can
+ * be used as a tick count, rather than a jiffies count.
+ */
+ if ((long)delta < 1)
+ delta = 1;
+ else if (delta > INT_MAX)
+ delta = INT_MAX;
+ return ((int)delta);
+}
+
int
-mod_timer(struct timer_list *timer, int expires)
+mod_timer(struct timer_list *timer, unsigned long expires)
{
int ret;
@@ -2069,20 +2221,16 @@ SYSINIT(linux_timer, SI_SUB_DRIVERS, SI_ORDER_FIRST, linux_timer_init, NULL);
void
linux_complete_common(struct completion *c, int all)
{
- int wakeup_swapper;
-
sleepq_lock(c);
if (all) {
c->done = UINT_MAX;
- wakeup_swapper = sleepq_broadcast(c, SLEEPQ_SLEEP, 0, 0);
+ sleepq_broadcast(c, SLEEPQ_SLEEP, 0, 0);
} else {
if (c->done != UINT_MAX)
c->done++;
- wakeup_swapper = sleepq_signal(c, SLEEPQ_SLEEP, 0, 0);
+ sleepq_signal(c, SLEEPQ_SLEEP, 0, 0);
}
sleepq_release(c);
- if (wakeup_swapper)
- kick_proc0();
}
/*
@@ -2135,12 +2283,12 @@ intr:
/*
* Time limited wait for done != 0 with or without signals.
*/
-int
-linux_wait_for_timeout_common(struct completion *c, int timeout, int flags)
+unsigned long
+linux_wait_for_timeout_common(struct completion *c, unsigned long timeout,
+ int flags)
{
struct task_struct *task;
- int end = jiffies + timeout;
- int error;
+ unsigned long end = jiffies + timeout, error;
if (SCHEDULER_STOPPED())
return (0);
@@ -2597,6 +2745,54 @@ io_mapping_create_wc(resource_size_t base, unsigned long size)
return (io_mapping_init_wc(mapping, base, size));
}
+/* We likely want a linuxkpi_device.c at some point. */
+bool
+device_can_wakeup(struct device *dev)
+{
+
+ if (dev == NULL)
+ return (false);
+ /*
+ * XXX-BZ iwlwifi queries it as part of enabling WoWLAN.
+ * Normally this would be based on a bool in dev->power.XXX.
+ * Check such as PCI PCIM_PCAP_*PME. We have no way to enable this yet.
+ * We may get away by directly calling into bsddev for as long as
+ * we can assume PCI only avoiding changing struct device breaking KBI.
+ */
+ pr_debug("%s:%d: not enabled; see comment.\n", __func__, __LINE__);
+ return (false);
+}
+
+static void
+devm_device_group_remove(struct device *dev, void *p)
+{
+ const struct attribute_group **dr = p;
+ const struct attribute_group *group = *dr;
+
+ sysfs_remove_group(&dev->kobj, group);
+}
+
+int
+lkpi_devm_device_add_group(struct device *dev,
+ const struct attribute_group *group)
+{
+ const struct attribute_group **dr;
+ int ret;
+
+ dr = devres_alloc(devm_device_group_remove, sizeof(*dr), GFP_KERNEL);
+ if (dr == NULL)
+ return (-ENOMEM);
+
+ ret = sysfs_create_group(&dev->kobj, group);
+ if (ret == 0) {
+ *dr = group;
+ devres_add(dev, dr);
+ } else
+ devres_free(dr);
+
+ return (ret);
+}
+
#if defined(__i386__) || defined(__amd64__)
bool linux_cpu_has_clflush;
struct cpuinfo_x86 boot_cpu_data;
@@ -2667,8 +2863,8 @@ linux_compat_init(void *arg)
boot_cpu_data.x86_model = CPUID_TO_MODEL(cpu_id);
boot_cpu_data.x86_vendor = x86_vendor;
- __cpu_data = mallocarray(mp_maxid + 1,
- sizeof(*__cpu_data), M_KMALLOC, M_WAITOK | M_ZERO);
+ __cpu_data = kmalloc_array(mp_maxid + 1,
+ sizeof(*__cpu_data), M_WAITOK | M_ZERO);
CPU_FOREACH(i) {
__cpu_data[i].x86_clflush_size = cpu_clflush_line_size;
__cpu_data[i].x86_max_cores = mp_ncpus;
@@ -2710,8 +2906,8 @@ linux_compat_init(void *arg)
* This is used by cpumask_of() (and possibly others in the future) for,
* e.g., drivers to pass hints to irq_set_affinity_hint().
*/
- static_single_cpu_mask = mallocarray(mp_maxid + 1,
- sizeof(static_single_cpu_mask), M_KMALLOC, M_WAITOK | M_ZERO);
+ static_single_cpu_mask = kmalloc_array(mp_maxid + 1,
+ sizeof(static_single_cpu_mask), M_WAITOK | M_ZERO);
/*
* When the number of CPUs reach a threshold, we start to save memory
@@ -2730,9 +2926,9 @@ linux_compat_init(void *arg)
* (_BITSET_BITS / 8)' bytes (for comparison with the
* overlapping scheme).
*/
- static_single_cpu_mask_lcs = mallocarray(mp_ncpus,
+ static_single_cpu_mask_lcs = kmalloc_array(mp_ncpus,
sizeof(*static_single_cpu_mask_lcs),
- M_KMALLOC, M_WAITOK | M_ZERO);
+ M_WAITOK | M_ZERO);
sscm_ptr = static_single_cpu_mask_lcs;
CPU_FOREACH(i) {
diff --git a/sys/compat/linuxkpi/common/src/linux_firmware.c b/sys/compat/linuxkpi/common/src/linux_firmware.c
index 17da91381280..12658df5ce83 100644
--- a/sys/compat/linuxkpi/common/src/linux_firmware.c
+++ b/sys/compat/linuxkpi/common/src/linux_firmware.c
@@ -88,17 +88,17 @@ _linuxkpi_request_firmware(const char *fw_name, const struct linuxkpi_firmware *
* way rather than adding more name-mangling-hacks here in the future
* (though we could if needed).
*/
- /* (1) Try any name removed of path. */
- fwimg = strrchr(fw_name, '/');
- if (fwimg != NULL)
- fwimg++;
- if (fwimg == NULL || *fwimg == '\0')
- fwimg = fw_name;
- fbdfw = firmware_get_flags(fwimg, flags);
- /* (2) Try the original name if we have not yet. */
- if (fbdfw == NULL && fwimg != fw_name) {
- fwimg = fw_name;
- fbdfw = firmware_get_flags(fwimg, flags);
+ /* (1) Try the original name. */
+ fbdfw = firmware_get_flags(fw_name, flags);
+ /* (2) Try any name removed of path, if we have not yet. */
+ if (fbdfw == NULL) {
+ fwimg = strrchr(fw_name, '/');
+ if (fwimg != NULL)
+ fwimg++;
+ if (fwimg == NULL || *fwimg == '\0')
+ fwimg = fw_name;
+ if (fwimg != fw_name)
+ fbdfw = firmware_get_flags(fwimg, flags);
}
/* (3) Flatten '/', '.' and '-' to '_' and try with adjusted name. */
if (fbdfw == NULL &&
diff --git a/sys/compat/linuxkpi/common/src/linux_folio.c b/sys/compat/linuxkpi/common/src/linux_folio.c
new file mode 100644
index 000000000000..c2af7792be04
--- /dev/null
+++ b/sys/compat/linuxkpi/common/src/linux_folio.c
@@ -0,0 +1,58 @@
+/*-
+ * Copyright (c) 2024-2025 The FreeBSD Foundation
+ * Copyright (c) 2024-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 <linux/gfp.h>
+#include <linux/mm.h>
+#include <linux/mm_types.h>
+#include <linux/page.h>
+#include <linux/pagevec.h>
+
+struct folio *
+folio_alloc(gfp_t gfp, unsigned int order)
+{
+ struct page *page;
+ struct folio *folio;
+
+ /*
+ * Allocated pages are wired already. There is no need to increase a
+ * refcount here.
+ */
+ page = alloc_pages(gfp | __GFP_COMP, order);
+ folio = (struct folio *)page;
+
+ return (folio);
+}
+
+void
+__folio_batch_release(struct folio_batch *fbatch)
+{
+ release_pages(fbatch->folios, folio_batch_count(fbatch));
+
+ folio_batch_reinit(fbatch);
+}
diff --git a/sys/compat/linuxkpi/common/src/linux_hdmi.c b/sys/compat/linuxkpi/common/src/linux_hdmi.c
index 947be761dfa4..fc47693e913c 100644
--- a/sys/compat/linuxkpi/common/src/linux_hdmi.c
+++ b/sys/compat/linuxkpi/common/src/linux_hdmi.c
@@ -21,6 +21,9 @@
* DEALINGS IN THE SOFTWARE.
*/
+#ifdef __linux__
+#include <drm/display/drm_dp.h>
+#endif
#include <linux/bitops.h>
#include <linux/bug.h>
#include <linux/errno.h>
@@ -381,12 +384,34 @@ static int hdmi_audio_infoframe_check_only(const struct hdmi_audio_infoframe *fr
*
* Returns 0 on success or a negative error code on failure.
*/
-int hdmi_audio_infoframe_check(struct hdmi_audio_infoframe *frame)
+int hdmi_audio_infoframe_check(const struct hdmi_audio_infoframe *frame)
{
return hdmi_audio_infoframe_check_only(frame);
}
EXPORT_SYMBOL(hdmi_audio_infoframe_check);
+static void
+hdmi_audio_infoframe_pack_payload(const struct hdmi_audio_infoframe *frame,
+ u8 *buffer)
+{
+ u8 channels;
+
+ if (frame->channels >= 2)
+ channels = frame->channels - 1;
+ else
+ channels = 0;
+
+ buffer[0] = ((frame->coding_type & 0xf) << 4) | (channels & 0x7);
+ buffer[1] = ((frame->sample_frequency & 0x7) << 2) |
+ (frame->sample_size & 0x3);
+ buffer[2] = frame->coding_type_ext & 0x1f;
+ buffer[3] = frame->channel_allocation;
+ buffer[4] = (frame->level_shift_value & 0xf) << 3;
+
+ if (frame->downmix_inhibit)
+ buffer[4] |= BIT(7);
+}
+
/**
* hdmi_audio_infoframe_pack_only() - write HDMI audio infoframe to binary buffer
* @frame: HDMI audio infoframe
@@ -404,7 +429,6 @@ EXPORT_SYMBOL(hdmi_audio_infoframe_check);
ssize_t hdmi_audio_infoframe_pack_only(const struct hdmi_audio_infoframe *frame,
void *buffer, size_t size)
{
- unsigned char channels;
u8 *ptr = buffer;
size_t length;
int ret;
@@ -420,28 +444,13 @@ ssize_t hdmi_audio_infoframe_pack_only(const struct hdmi_audio_infoframe *frame,
memset(buffer, 0, size);
- if (frame->channels >= 2)
- channels = frame->channels - 1;
- else
- channels = 0;
-
ptr[0] = frame->type;
ptr[1] = frame->version;
ptr[2] = frame->length;
ptr[3] = 0; /* checksum */
- /* start infoframe payload */
- ptr += HDMI_INFOFRAME_HEADER_SIZE;
-
- ptr[0] = ((frame->coding_type & 0xf) << 4) | (channels & 0x7);
- ptr[1] = ((frame->sample_frequency & 0x7) << 2) |
- (frame->sample_size & 0x3);
- ptr[2] = frame->coding_type_ext & 0x1f;
- ptr[3] = frame->channel_allocation;
- ptr[4] = (frame->level_shift_value & 0xf) << 3;
-
- if (frame->downmix_inhibit)
- ptr[4] |= BIT(7);
+ hdmi_audio_infoframe_pack_payload(frame,
+ ptr + HDMI_INFOFRAME_HEADER_SIZE);
hdmi_infoframe_set_checksum(buffer, length);
@@ -479,6 +488,45 @@ ssize_t hdmi_audio_infoframe_pack(struct hdmi_audio_infoframe *frame,
}
EXPORT_SYMBOL(hdmi_audio_infoframe_pack);
+#ifdef __linux__
+/**
+ * hdmi_audio_infoframe_pack_for_dp - Pack a HDMI Audio infoframe for DisplayPort
+ *
+ * @frame: HDMI Audio infoframe
+ * @sdp: Secondary data packet for DisplayPort.
+ * @dp_version: DisplayPort version to be encoded in the header
+ *
+ * Packs a HDMI Audio Infoframe to be sent over DisplayPort. This function
+ * fills the secondary data packet to be used for DisplayPort.
+ *
+ * Return: Number of total written bytes or a negative errno on failure.
+ */
+ssize_t
+hdmi_audio_infoframe_pack_for_dp(const struct hdmi_audio_infoframe *frame,
+ struct dp_sdp *sdp, u8 dp_version)
+{
+ int ret;
+
+ ret = hdmi_audio_infoframe_check(frame);
+ if (ret)
+ return ret;
+
+ memset(sdp->db, 0, sizeof(sdp->db));
+
+ /* Secondary-data packet header */
+ sdp->sdp_header.HB0 = 0;
+ sdp->sdp_header.HB1 = frame->type;
+ sdp->sdp_header.HB2 = DP_SDP_AUDIO_INFOFRAME_HB2;
+ sdp->sdp_header.HB3 = (dp_version & 0x3f) << 2;
+
+ hdmi_audio_infoframe_pack_payload(frame, sdp->db);
+
+ /* Return size = frame length + four HB for sdp_header */
+ return frame->length + 4;
+}
+EXPORT_SYMBOL(hdmi_audio_infoframe_pack_for_dp);
+#endif
+
/**
* hdmi_vendor_infoframe_init() - initialize an HDMI vendor infoframe
* @frame: HDMI vendor infoframe
diff --git a/sys/compat/linuxkpi/common/src/linux_i2c.c b/sys/compat/linuxkpi/common/src/linux_i2c.c
index 60f7737cf6ec..f18570202f74 100644
--- a/sys/compat/linuxkpi/common/src/linux_i2c.c
+++ b/sys/compat/linuxkpi/common/src/linux_i2c.c
@@ -85,12 +85,12 @@ lkpi_iic_attach(device_t dev)
struct lkpi_iic_softc *sc;
sc = device_get_softc(dev);
- sc->iicbus = device_add_child(dev, "iicbus", -1);
+ sc->iicbus = device_add_child(dev, "iicbus", DEVICE_UNIT_ANY);
if (sc->iicbus == NULL) {
device_printf(dev, "Couldn't add iicbus child, aborting\n");
return (ENXIO);
}
- bus_generic_attach(dev);
+ bus_attach_children(dev);
return (0);
}
@@ -316,7 +316,6 @@ int
lkpi_i2c_add_adapter(struct i2c_adapter *adapter)
{
device_t lkpi_iic;
- int error;
if (adapter->name[0] == '\0')
return (-EINVAL);
@@ -324,7 +323,8 @@ lkpi_i2c_add_adapter(struct i2c_adapter *adapter)
device_printf(adapter->dev.parent->bsddev,
"Adding i2c adapter %s\n", adapter->name);
sx_xlock(&lkpi_sx_i2c);
- lkpi_iic = device_add_child(adapter->dev.parent->bsddev, "lkpi_iic", -1);
+ lkpi_iic = device_add_child(adapter->dev.parent->bsddev, "lkpi_iic",
+ DEVICE_UNIT_ANY);
if (lkpi_iic == NULL) {
device_printf(adapter->dev.parent->bsddev, "Couldn't add lkpi_iic\n");
sx_xunlock(&lkpi_sx_i2c);
@@ -332,14 +332,8 @@ lkpi_i2c_add_adapter(struct i2c_adapter *adapter)
}
bus_topo_lock();
- error = bus_generic_attach(adapter->dev.parent->bsddev);
+ bus_attach_children(adapter->dev.parent->bsddev);
bus_topo_unlock();
- if (error) {
- device_printf(adapter->dev.parent->bsddev,
- "failed to attach child: error %d\n", error);
- sx_xunlock(&lkpi_sx_i2c);
- return (ENXIO);
- }
LKPI_IIC_ADD_ADAPTER(lkpi_iic, adapter);
sx_xunlock(&lkpi_sx_i2c);
return (0);
diff --git a/sys/compat/linuxkpi/common/src/linux_i2cbb.c b/sys/compat/linuxkpi/common/src/linux_i2cbb.c
index 1ebc0b597c4d..48a018ec2533 100644
--- a/sys/compat/linuxkpi/common/src/linux_i2cbb.c
+++ b/sys/compat/linuxkpi/common/src/linux_i2cbb.c
@@ -90,12 +90,12 @@ lkpi_iicbb_attach(device_t dev)
struct lkpi_iicbb_softc *sc;
sc = device_get_softc(dev);
- sc->iicbb = device_add_child(dev, "iicbb", -1);
+ sc->iicbb = device_add_child(dev, "iicbb", DEVICE_UNIT_ANY);
if (sc->iicbb == NULL) {
device_printf(dev, "Couldn't add iicbb child, aborting\n");
return (ENXIO);
}
- bus_generic_attach(dev);
+ bus_attach_children(dev);
return (0);
}
@@ -303,13 +303,13 @@ int
lkpi_i2c_bit_add_bus(struct i2c_adapter *adapter)
{
device_t lkpi_iicbb;
- int error;
if (bootverbose)
device_printf(adapter->dev.parent->bsddev,
"Adding i2c adapter %s\n", adapter->name);
sx_xlock(&lkpi_sx_i2cbb);
- lkpi_iicbb = device_add_child(adapter->dev.parent->bsddev, "lkpi_iicbb", -1);
+ lkpi_iicbb = device_add_child(adapter->dev.parent->bsddev, "lkpi_iicbb",
+ DEVICE_UNIT_ANY);
if (lkpi_iicbb == NULL) {
device_printf(adapter->dev.parent->bsddev, "Couldn't add lkpi_iicbb\n");
sx_xunlock(&lkpi_sx_i2cbb);
@@ -317,14 +317,8 @@ lkpi_i2c_bit_add_bus(struct i2c_adapter *adapter)
}
bus_topo_lock();
- error = bus_generic_attach(adapter->dev.parent->bsddev);
+ bus_attach_children(adapter->dev.parent->bsddev);
bus_topo_unlock();
- if (error) {
- device_printf(adapter->dev.parent->bsddev,
- "failed to attach child: error %d\n", error);
- sx_xunlock(&lkpi_sx_i2cbb);
- return (ENXIO);
- }
LKPI_IIC_ADD_ADAPTER(lkpi_iicbb, adapter);
sx_xunlock(&lkpi_sx_i2cbb);
return (0);
diff --git a/sys/compat/linuxkpi/common/src/linux_idr.c b/sys/compat/linuxkpi/common/src/linux_idr.c
index dc64da0d7cf5..664177835c85 100644
--- a/sys/compat/linuxkpi/common/src/linux_idr.c
+++ b/sys/compat/linuxkpi/common/src/linux_idr.c
@@ -34,8 +34,7 @@
#include <sys/sysctl.h>
#include <sys/lock.h>
#include <sys/mutex.h>
-
-#include <machine/stdarg.h>
+#include <sys/stdarg.h>
#include <linux/bitmap.h>
#include <linux/kobject.h>
@@ -69,7 +68,7 @@ idr_preload_dequeue_locked(struct linux_idr_cache *lic)
struct idr_layer *retval;
/* check if wrong thread is trying to dequeue */
- if (mtx_owned(&lic->lock.m) == 0)
+ if (mtx_owned(&lic->lock) == 0)
return (NULL);
retval = lic->head;
@@ -178,6 +177,14 @@ idr_destroy(struct idr *idr)
{
struct idr_layer *il, *iln;
+ /*
+ * This idr can be reused, and this function might be called multiple times
+ * without a idr_init(). Check if this is the case. If we do not do this
+ * then the mutex will panic while asserting that it is valid.
+ */
+ if (mtx_initialized(&idr->lock) == 0)
+ return;
+
idr_remove_all(idr);
mtx_lock(&idr->lock);
for (il = idr->free; il != NULL; il = iln) {
@@ -802,4 +809,5 @@ ida_destroy(struct ida *ida)
{
idr_destroy(&ida->idr);
free(ida->free_bitmap, M_IDR);
+ ida->free_bitmap = NULL;
}
diff --git a/sys/compat/linuxkpi/common/src/linux_kobject.c b/sys/compat/linuxkpi/common/src/linux_kobject.c
index 02f8b8d5b709..2b9d3dcffa4b 100644
--- a/sys/compat/linuxkpi/common/src/linux_kobject.c
+++ b/sys/compat/linuxkpi/common/src/linux_kobject.c
@@ -122,10 +122,10 @@ kobject_add_complete(struct kobject *kobj)
for (attr = t->default_attrs; *attr != NULL; attr++) {
error = sysfs_create_file(kobj, *attr);
- if (error)
+ if (error != 0)
break;
}
- if (error)
+ if (error != 0)
sysfs_remove_dir(kobj);
}
diff --git a/sys/compat/linuxkpi/common/src/linux_netdev.c b/sys/compat/linuxkpi/common/src/linux_netdev.c
index fe00e929c168..ce9153614104 100644
--- a/sys/compat/linuxkpi/common/src/linux_netdev.c
+++ b/sys/compat/linuxkpi/common/src/linux_netdev.c
@@ -63,22 +63,22 @@ SYSCTL_INT(_compat_linuxkpi, OID_AUTO, debug_napi, CTLFLAG_RWTUN,
#define DNAPI_DIRECT_DISPATCH 0x1000
#define NAPI_TRACE(_n) if (debug_napi & DNAPI_TRACE) \
- printf("NAPI_TRACE %s:%d %u %p (%#jx %b)\n", __func__, __LINE__, \
- (unsigned int)ticks, _n, (uintmax_t)(_n)->state, \
+ printf("NAPI_TRACE %s:%d %lu %p (%#jx %b)\n", __func__, __LINE__, \
+ jiffies, _n, (uintmax_t)(_n)->state, \
(int)(_n)->state, LKPI_NAPI_FLAGS)
#define NAPI_TRACE2D(_n, _d) if (debug_napi & DNAPI_TRACE) \
- printf("NAPI_TRACE %s:%d %u %p (%#jx %b) %d\n", __func__, __LINE__, \
- (unsigned int)ticks, _n, (uintmax_t)(_n)->state, \
+ printf("NAPI_TRACE %s:%d %lu %p (%#jx %b) %d\n", __func__, __LINE__, \
+ jiffies, _n, (uintmax_t)(_n)->state, \
(int)(_n)->state, LKPI_NAPI_FLAGS, _d)
#define NAPI_TRACE_TASK(_n, _p, _c) if (debug_napi & DNAPI_TRACE_TASK) \
- printf("NAPI_TRACE %s:%d %u %p (%#jx %b) pending %d count %d " \
+ printf("NAPI_TRACE %s:%d %lu %p (%#jx %b) pending %d count %d " \
"rx_count %d\n", __func__, __LINE__, \
- (unsigned int)ticks, _n, (uintmax_t)(_n)->state, \
+ jiffies, _n, (uintmax_t)(_n)->state, \
(int)(_n)->state, LKPI_NAPI_FLAGS, _p, _c, (_n)->rx_count)
#define NAPI_TODO() if (debug_napi & DNAPI_TODO) \
- printf("NAPI_TODO %s:%d %d\n", __func__, __LINE__, ticks)
+ printf("NAPI_TODO %s:%d %lu\n", __func__, __LINE__, jiffies)
#define NAPI_IMPROVE() if (debug_napi & DNAPI_IMPROVE) \
- printf("NAPI_IMPROVE %s:%d %d\n", __func__, __LINE__, ticks)
+ printf("NAPI_IMPROVE %s:%d %lu\n", __func__, __LINE__, jiffies)
#define NAPI_DIRECT_DISPATCH() ((debug_napi & DNAPI_DIRECT_DISPATCH) != 0)
#else
@@ -409,7 +409,8 @@ linuxkpi_alloc_netdev(size_t len, const char *name, uint32_t flags,
/* This needs extending as we support more. */
- setup_func(ndev);
+ if (setup_func != NULL)
+ setup_func(ndev);
return (ndev);
}
diff --git a/sys/compat/linuxkpi/common/src/linux_page.c b/sys/compat/linuxkpi/common/src/linux_page.c
index 8b78a3739f25..ebb92eacbf9a 100644
--- a/sys/compat/linuxkpi/common/src/linux_page.c
+++ b/sys/compat/linuxkpi/common/src/linux_page.c
@@ -83,7 +83,7 @@ si_meminfo(struct sysinfo *si)
}
void *
-linux_page_address(struct page *page)
+linux_page_address(const struct page *page)
{
if (page->object != kernel_object) {
@@ -117,7 +117,8 @@ linux_alloc_pages(gfp_t flags, unsigned int order)
page = vm_page_alloc_noobj_contig(req, npages, 0, pmax,
PAGE_SIZE, 0, VM_MEMATTR_DEFAULT);
if (page == NULL) {
- if (flags & M_WAITOK) {
+ if ((flags & (M_WAITOK | __GFP_NORETRY)) ==
+ M_WAITOK) {
int err = vm_page_reclaim_contig(req,
npages, 0, pmax, PAGE_SIZE, 0);
if (err == ENOMEM)
@@ -164,8 +165,24 @@ linux_free_pages(struct page *page, unsigned int order)
for (x = 0; x != npages; x++) {
vm_page_t pgo = page + x;
- if (vm_page_unwire_noq(pgo))
- vm_page_free(pgo);
+ /*
+ * The "free page" function is used in several
+ * contexts.
+ *
+ * Some pages are allocated by `linux_alloc_pages()`
+ * above, but not all of them are. For instance in the
+ * DRM drivers, some pages come from
+ * `shmem_read_mapping_page_gfp()`.
+ *
+ * That's why we need to check if the page is managed
+ * or not here.
+ */
+ if ((pgo->oflags & VPO_UNMANAGED) == 0) {
+ vm_page_unwire(pgo, PQ_ACTIVE);
+ } else {
+ if (vm_page_unwire_noq(pgo))
+ vm_page_free(pgo);
+ }
}
} else {
vm_offset_t vaddr;
@@ -176,18 +193,27 @@ linux_free_pages(struct page *page, unsigned int order)
}
}
+void
+linux_release_pages(release_pages_arg arg, int nr)
+{
+ int i;
+
+ CTASSERT(offsetof(struct folio, page) == 0);
+
+ for (i = 0; i < nr; i++)
+ __free_page(arg.pages[i]);
+}
+
vm_offset_t
linux_alloc_kmem(gfp_t flags, unsigned int order)
{
size_t size = ((size_t)PAGE_SIZE) << order;
void *addr;
- if ((flags & GFP_DMA32) == 0) {
- addr = kmem_malloc(size, flags & GFP_NATIVE_MASK);
- } else {
- addr = kmem_alloc_contig(size, flags & GFP_NATIVE_MASK, 0,
- BUS_SPACE_MAXADDR_32BIT, PAGE_SIZE, 0, VM_MEMATTR_DEFAULT);
- }
+ addr = kmem_alloc_contig(size, flags & GFP_NATIVE_MASK, 0,
+ ((flags & GFP_DMA32) == 0) ? -1UL : BUS_SPACE_MAXADDR_32BIT,
+ PAGE_SIZE, 0, VM_MEMATTR_DEFAULT);
+
return ((vm_offset_t)addr);
}
@@ -277,8 +303,8 @@ get_user_pages_remote(struct task_struct *task, struct mm_struct *mm,
}
long
-get_user_pages(unsigned long start, unsigned long nr_pages,
- unsigned int gup_flags, struct page **pages, struct vm_area_struct **vmas)
+lkpi_get_user_pages(unsigned long start, unsigned long nr_pages,
+ unsigned int gup_flags, struct page **pages)
{
vm_map_t map;
@@ -297,23 +323,27 @@ vm_fault_t
lkpi_vmf_insert_pfn_prot_locked(struct vm_area_struct *vma, unsigned long addr,
unsigned long pfn, pgprot_t prot)
{
+ struct pctrie_iter pages;
vm_object_t vm_obj = vma->vm_obj;
vm_object_t tmp_obj;
vm_page_t page;
vm_pindex_t pindex;
VM_OBJECT_ASSERT_WLOCKED(vm_obj);
+ vm_page_iter_init(&pages, vm_obj);
pindex = OFF_TO_IDX(addr - vma->vm_start);
if (vma->vm_pfn_count == 0)
vma->vm_pfn_first = pindex;
MPASS(pindex <= OFF_TO_IDX(vma->vm_end));
retry:
- page = vm_page_grab(vm_obj, pindex, VM_ALLOC_NOCREAT);
+ page = vm_page_grab_iter(vm_obj, pindex, VM_ALLOC_NOCREAT, &pages);
if (page == NULL) {
page = PHYS_TO_VM_PAGE(IDX_TO_OFF(pfn));
- if (!vm_page_busy_acquire(page, VM_ALLOC_WAITFAIL))
+ if (!vm_page_busy_acquire(page, VM_ALLOC_WAITFAIL)) {
+ pctrie_iter_reset(&pages);
goto retry;
+ }
if (page->object != NULL) {
tmp_obj = page->object;
vm_page_xunbusy(page);
@@ -337,10 +367,11 @@ retry:
vm_page_remove(page);
}
VM_OBJECT_WUNLOCK(tmp_obj);
+ pctrie_iter_reset(&pages);
VM_OBJECT_WLOCK(vm_obj);
goto retry;
}
- if (vm_page_insert(page, vm_obj, pindex)) {
+ if (vm_page_iter_insert(page, vm_obj, pindex, &pages) != 0) {
vm_page_xunbusy(page);
return (VM_FAULT_OOM);
}
@@ -418,27 +449,13 @@ lkpi_io_mapping_map_user(struct io_mapping *iomap,
*/
void
lkpi_unmap_mapping_range(void *obj, loff_t const holebegin __unused,
- loff_t const holelen, int even_cows __unused)
+ loff_t const holelen __unused, int even_cows __unused)
{
vm_object_t devobj;
- vm_page_t page;
- int i, page_count;
devobj = cdev_pager_lookup(obj);
if (devobj != NULL) {
- page_count = OFF_TO_IDX(holelen);
-
- VM_OBJECT_WLOCK(devobj);
-retry:
- for (i = 0; i < page_count; i++) {
- page = vm_page_lookup(devobj, i);
- if (page == NULL)
- continue;
- if (!vm_page_busy_acquire(page, VM_ALLOC_WAITFAIL))
- goto retry;
- cdev_pager_free_page(devobj, page);
- }
- VM_OBJECT_WUNLOCK(devobj);
+ cdev_mgtdev_pager_free_pages(devobj);
vm_object_deallocate(devobj);
}
}
diff --git a/sys/compat/linuxkpi/common/src/linux_pci.c b/sys/compat/linuxkpi/common/src/linux_pci.c
index 825ebe319b1a..55202da00440 100644
--- a/sys/compat/linuxkpi/common/src/linux_pci.c
+++ b/sys/compat/linuxkpi/common/src/linux_pci.c
@@ -43,13 +43,13 @@
#include <sys/pctrie.h>
#include <sys/rman.h>
#include <sys/rwlock.h>
+#include <sys/stdarg.h>
#include <vm/vm.h>
#include <vm/pmap.h>
#include <machine/bus.h>
#include <machine/resource.h>
-#include <machine/stdarg.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pci_private.h>
@@ -287,18 +287,22 @@ linux_pci_find(device_t dev, const struct pci_device_id **idp)
struct pci_dev *
lkpi_pci_get_device(uint16_t vendor, uint16_t device, struct pci_dev *odev)
{
- struct pci_dev *pdev;
+ struct pci_dev *pdev, *found;
KASSERT(odev == NULL, ("%s: odev argument not yet supported\n", __func__));
+ found = NULL;
spin_lock(&pci_lock);
list_for_each_entry(pdev, &pci_devices, links) {
- if (pdev->vendor == vendor && pdev->device == device)
+ if (pdev->vendor == vendor && pdev->device == device) {
+ found = pdev;
break;
+ }
}
+ pci_dev_get(found);
spin_unlock(&pci_lock);
- return (pdev);
+ return (found);
}
static void
@@ -309,9 +313,18 @@ lkpi_pci_dev_release(struct device *dev)
spin_lock_destroy(&dev->devres_lock);
}
-static void
+static int
lkpifill_pci_dev(device_t dev, struct pci_dev *pdev)
{
+ int error;
+
+ error = kobject_init_and_add(&pdev->dev.kobj, &linux_dev_ktype,
+ &linux_root_device.kobj, device_get_nameunit(dev));
+ if (error != 0) {
+ printf("%s:%d: kobject_init_and_add returned %d\n",
+ __func__, __LINE__, error);
+ return (error);
+ }
pdev->devfn = PCI_DEVFN(pci_get_slot(dev), pci_get_function(dev));
pdev->vendor = pci_get_vendor(dev);
@@ -341,12 +354,10 @@ lkpifill_pci_dev(device_t dev, struct pci_dev *pdev)
pdev->msi_desc = malloc(pci_msi_count(dev) *
sizeof(*pdev->msi_desc), M_DEVBUF, M_WAITOK | M_ZERO);
- kobject_init(&pdev->dev.kobj, &linux_dev_ktype);
- kobject_set_name(&pdev->dev.kobj, device_get_nameunit(dev));
- kobject_add(&pdev->dev.kobj, &linux_root_device.kobj,
- kobject_name(&pdev->dev.kobj));
spin_lock_init(&pdev->dev.devres_lock);
INIT_LIST_HEAD(&pdev->dev.devres_head);
+
+ return (0);
}
static void
@@ -374,9 +385,14 @@ struct pci_dev *
lkpinew_pci_dev(device_t dev)
{
struct pci_dev *pdev;
+ int error;
pdev = malloc(sizeof(*pdev), M_DEVBUF, M_WAITOK|M_ZERO);
- lkpifill_pci_dev(dev, pdev);
+ error = lkpifill_pci_dev(dev, pdev);
+ if (error != 0) {
+ free(pdev, M_DEVBUF);
+ return (NULL);
+ }
pdev->dev.release = lkpinew_pci_dev_release;
return (pdev);
@@ -401,6 +417,24 @@ lkpi_pci_get_class(unsigned int class, struct pci_dev *from)
}
struct pci_dev *
+lkpi_pci_get_base_class(unsigned int baseclass, struct pci_dev *from)
+{
+ device_t dev;
+ device_t devfrom = NULL;
+ struct pci_dev *pdev;
+
+ if (from != NULL)
+ devfrom = from->dev.bsddev;
+
+ dev = pci_find_base_class_from(baseclass, devfrom);
+ if (dev == NULL)
+ return (NULL);
+
+ pdev = lkpinew_pci_dev(dev);
+ return (pdev);
+}
+
+struct pci_dev *
lkpi_pci_get_domain_bus_and_slot(int domain, unsigned int bus,
unsigned int devfn)
{
@@ -507,7 +541,10 @@ linux_pci_attach_device(device_t dev, struct pci_driver *pdrv,
device_set_ivars(dev, dinfo);
}
- lkpifill_pci_dev(dev, pdev);
+ error = lkpifill_pci_dev(dev, pdev);
+ if (error != 0)
+ return (error);
+
if (isdrm)
PCI_GET_ID(device_get_parent(parent), parent, PCI_ID_RID, &rid);
else
@@ -755,7 +792,8 @@ _lkpi_pci_iomap(struct pci_dev *pdev, int bar, int mmio_size __unused)
}
void *
-linuxkpi_pci_iomap(struct pci_dev *pdev, int mmio_bar, int mmio_size)
+linuxkpi_pci_iomap_range(struct pci_dev *pdev, int mmio_bar,
+ unsigned long mmio_off, unsigned long mmio_size)
{
struct resource *res;
@@ -765,17 +803,32 @@ linuxkpi_pci_iomap(struct pci_dev *pdev, int mmio_bar, int mmio_size)
/* This is a FreeBSD extension so we can use bus_*(). */
if (pdev->want_iomap_res)
return (res);
- return ((void *)rman_get_bushandle(res));
+ MPASS(mmio_off < rman_get_size(res));
+ return ((void *)(rman_get_bushandle(res) + mmio_off));
+}
+
+void *
+linuxkpi_pci_iomap(struct pci_dev *pdev, int mmio_bar, int mmio_size)
+{
+ return (linuxkpi_pci_iomap_range(pdev, mmio_bar, 0, mmio_size));
}
void
linuxkpi_pci_iounmap(struct pci_dev *pdev, void *res)
{
struct pci_mmio_region *mmio, *p;
+ bus_space_handle_t bh = (bus_space_handle_t)res;
TAILQ_FOREACH_SAFE(mmio, &pdev->mmio, next, p) {
- if (res != (void *)rman_get_bushandle(mmio->res))
- continue;
+ if (pdev->want_iomap_res) {
+ if (res != mmio->res)
+ continue;
+ } else {
+ if (bh < rman_get_bushandle(mmio->res) ||
+ bh >= rman_get_bushandle(mmio->res) +
+ rman_get_size(mmio->res))
+ continue;
+ }
bus_release_resource(pdev->dev.bsddev,
mmio->type, mmio->rid, mmio->res);
TAILQ_REMOVE(&pdev->mmio, mmio, next);
@@ -976,10 +1029,10 @@ linux_pci_register_driver(struct pci_driver *pdrv)
{
devclass_t dc;
- dc = devclass_find("pci");
+ pdrv->isdrm = strcmp(pdrv->name, "drmn") == 0;
+ dc = pdrv->isdrm ? devclass_create("vgapci") : devclass_find("pci");
if (dc == NULL)
return (-ENXIO);
- pdrv->isdrm = false;
return (_linux_pci_register_driver(pdrv, dc));
}
@@ -1166,7 +1219,7 @@ linux_pci_unregister_driver(struct pci_driver *pdrv)
{
devclass_t bus;
- bus = devclass_find("pci");
+ bus = devclass_find(pdrv->isdrm ? "vgapci" : "pci");
spin_lock(&pci_lock);
list_del(&pdrv->node);
@@ -1290,7 +1343,7 @@ out:
if (error == 0 && pdev->msi_enabled)
return (pdev->dev.irq_end - pdev->dev.irq_start);
}
- if (flags & PCI_IRQ_LEGACY) {
+ if (flags & PCI_IRQ_INTX) {
if (pdev->irq)
return (1);
}
@@ -1437,14 +1490,19 @@ linux_dma_map_phys_common(struct device *dev, vm_paddr_t phys, size_t len,
}
nseg = -1;
- if (_bus_dmamap_load_phys(obj->dmat, obj->dmamap, phys, len,
- BUS_DMA_NOWAIT, &seg, &nseg) != 0) {
+ error = _bus_dmamap_load_phys(obj->dmat, obj->dmamap, phys, len,
+ BUS_DMA_NOWAIT, &seg, &nseg);
+ if (error != 0) {
bus_dmamap_destroy(obj->dmat, obj->dmamap);
DMA_PRIV_UNLOCK(priv);
uma_zfree(linux_dma_obj_zone, obj);
counter_u64_add(lkpi_pci_nseg1_fail, 1);
- if (linuxkpi_debug)
+ if (linuxkpi_debug) {
+ device_printf(dev->bsddev, "%s: _bus_dmamap_load_phys "
+ "error %d, phys %#018jx len %zu\n", __func__,
+ error, (uintmax_t)phys, len);
dump_stack();
+ }
return (0);
}
@@ -1472,17 +1530,34 @@ linux_dma_map_phys_common(struct device *dev __unused, vm_paddr_t phys,
#endif
dma_addr_t
-linux_dma_map_phys(struct device *dev, vm_paddr_t phys, size_t len)
+lkpi_dma_map_phys(struct device *dev, vm_paddr_t phys, size_t len,
+ enum dma_data_direction direction, unsigned long attrs)
{
struct linux_dma_priv *priv;
+ dma_addr_t dma;
priv = dev->dma_priv;
- return (linux_dma_map_phys_common(dev, phys, len, priv->dmat));
+ dma = linux_dma_map_phys_common(dev, phys, len, priv->dmat);
+ if (dma_mapping_error(dev, dma))
+ return (dma);
+
+ if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
+ dma_sync_single_for_device(dev, dma, len, direction);
+
+ return (dma);
+}
+
+/* For backward compat only so we can MFC this. Remove before 15. */
+dma_addr_t
+linux_dma_map_phys(struct device *dev, vm_paddr_t phys, size_t len)
+{
+ return (lkpi_dma_map_phys(dev, phys, len, DMA_NONE, 0));
}
#if defined(__i386__) || defined(__amd64__) || defined(__aarch64__)
void
-linux_dma_unmap(struct device *dev, dma_addr_t dma_addr, size_t len)
+lkpi_dma_unmap(struct device *dev, dma_addr_t dma_addr, size_t len,
+ enum dma_data_direction direction, unsigned long attrs)
{
struct linux_dma_priv *priv;
struct linux_dma_obj *obj;
@@ -1499,6 +1574,10 @@ linux_dma_unmap(struct device *dev, dma_addr_t dma_addr, size_t len)
return;
}
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);
+
bus_dmamap_unload(obj->dmat, obj->dmamap);
bus_dmamap_destroy(obj->dmat, obj->dmamap);
DMA_PRIV_UNLOCK(priv);
@@ -1507,11 +1586,19 @@ linux_dma_unmap(struct device *dev, dma_addr_t dma_addr, size_t len)
}
#else
void
-linux_dma_unmap(struct device *dev, dma_addr_t dma_addr, size_t len)
+lkpi_dma_unmap(struct device *dev, dma_addr_t dma_addr, size_t len,
+ enum dma_data_direction direction, unsigned long attrs)
{
}
#endif
+/* For backward compat only so we can MFC this. Remove before 15. */
+void
+linux_dma_unmap(struct device *dev, dma_addr_t dma_addr, size_t len)
+{
+ lkpi_dma_unmap(dev, dma_addr, len, DMA_NONE, 0);
+}
+
void *
linux_dma_alloc_coherent(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t flag)
@@ -1613,7 +1700,7 @@ linuxkpi_dma_sync(struct device *dev, dma_addr_t dma_addr, size_t size,
int
linux_dma_map_sg_attrs(struct device *dev, struct scatterlist *sgl, int nents,
- enum dma_data_direction direction, unsigned long attrs __unused)
+ enum dma_data_direction direction, unsigned long attrs)
{
struct linux_dma_priv *priv;
struct scatterlist *sg;
@@ -1647,6 +1734,9 @@ linux_dma_map_sg_attrs(struct device *dev, struct scatterlist *sgl, int nents,
sg_dma_address(sg) = seg.ds_addr;
}
+ if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) != 0)
+ goto skip_sync;
+
switch (direction) {
case DMA_BIDIRECTIONAL:
bus_dmamap_sync(priv->dmat, sgl->dma_map, BUS_DMASYNC_PREWRITE);
@@ -1660,6 +1750,7 @@ linux_dma_map_sg_attrs(struct device *dev, struct scatterlist *sgl, int nents,
default:
break;
}
+skip_sync:
DMA_PRIV_UNLOCK(priv);
@@ -1669,7 +1760,7 @@ linux_dma_map_sg_attrs(struct device *dev, struct scatterlist *sgl, int nents,
void
linux_dma_unmap_sg_attrs(struct device *dev, struct scatterlist *sgl,
int nents __unused, enum dma_data_direction direction,
- unsigned long attrs __unused)
+ unsigned long attrs)
{
struct linux_dma_priv *priv;
@@ -1677,6 +1768,9 @@ linux_dma_unmap_sg_attrs(struct device *dev, struct scatterlist *sgl,
DMA_PRIV_LOCK(priv);
+ if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) != 0)
+ goto skip_sync;
+
switch (direction) {
case DMA_BIDIRECTIONAL:
bus_dmamap_sync(priv->dmat, sgl->dma_map, BUS_DMASYNC_POSTREAD);
@@ -1691,6 +1785,7 @@ linux_dma_unmap_sg_attrs(struct device *dev, struct scatterlist *sgl,
default:
break;
}
+skip_sync:
bus_dmamap_unload(priv->dmat, sgl->dma_map);
bus_dmamap_destroy(priv->dmat, sgl->dma_map);
diff --git a/sys/compat/linuxkpi/common/src/linux_rcu.c b/sys/compat/linuxkpi/common/src/linux_rcu.c
index 335708b6747f..c0b864d269b3 100644
--- a/sys/compat/linuxkpi/common/src/linux_rcu.c
+++ b/sys/compat/linuxkpi/common/src/linux_rcu.c
@@ -2,6 +2,10 @@
* Copyright (c) 2016 Matthew Macy (mmacy@mattmacy.io)
* Copyright (c) 2017-2021 Hans Petter Selasky (hselasky@freebsd.org)
* All rights reserved.
+ * Copyright (c) 2024 The FreeBSD Foundation
+ *
+ * Portions of this software were developed by Björn Zeeb
+ * 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
@@ -41,6 +45,7 @@
#include <ck_epoch.h>
#include <linux/rcupdate.h>
+#include <linux/sched.h>
#include <linux/srcu.h>
#include <linux/slab.h>
#include <linux/kernel.h>
@@ -244,6 +249,33 @@ linux_rcu_read_unlock(unsigned type)
sched_unpin();
}
+bool
+linux_rcu_read_lock_held(unsigned type)
+{
+#ifdef INVARINATS
+ struct linux_epoch_record *record __diagused;
+ struct task_struct *ts;
+
+ MPASS(type < RCU_TYPE_MAX);
+
+ if (RCU_SKIP())
+ return (false);
+
+ if (__current_unallocated(curthread))
+ return (false);
+
+ ts = current;
+ if (ts->rcu_recurse[type] == 0)
+ return (false);
+
+ MPASS(curthread->td_pinned != 0);
+ MPASS((record = &DPCPU_GET(linux_epoch_record[type])) &&
+ record->epoch_record.active != 0);
+#endif
+
+ return (true);
+}
+
static void
linux_synchronize_rcu_cb(ck_epoch_t *epoch __unused, ck_epoch_record_t *epoch_record, void *arg __unused)
{
diff --git a/sys/compat/linuxkpi/common/src/linux_schedule.c b/sys/compat/linuxkpi/common/src/linux_schedule.c
index 66b339bfbdbd..c6b7a2ebbd66 100644
--- a/sys/compat/linuxkpi/common/src/linux_schedule.c
+++ b/sys/compat/linuxkpi/common/src/linux_schedule.c
@@ -38,29 +38,47 @@
#include <linux/spinlock.h>
#include <linux/wait.h>
+/*
+ * Convert a relative time in jiffies to a tick count, suitable for use with
+ * native FreeBSD interfaces (callouts, sleepqueues, etc.).
+ */
+static int
+linux_jiffies_timeout_to_ticks(long timeout)
+{
+ if (timeout < 1)
+ return (1);
+ else if (timeout == MAX_SCHEDULE_TIMEOUT)
+ return (0);
+ else if (timeout > INT_MAX)
+ return (INT_MAX);
+ else
+ return (timeout);
+}
+
static int
linux_add_to_sleepqueue(void *wchan, struct task_struct *task,
- const char *wmesg, int timeout, int state)
+ const char *wmesg, long timeout, int state)
{
- int flags, ret;
+ int flags, ret, stimeout;
MPASS((state & ~(TASK_PARKED | TASK_NORMAL)) == 0);
flags = SLEEPQ_SLEEP | ((state & TASK_INTERRUPTIBLE) != 0 ?
SLEEPQ_INTERRUPTIBLE : 0);
+ stimeout = linux_jiffies_timeout_to_ticks(timeout);
sleepq_add(wchan, NULL, wmesg, flags, 0);
- if (timeout != 0)
- sleepq_set_timeout(wchan, timeout);
+ if (stimeout != 0)
+ sleepq_set_timeout(wchan, stimeout);
DROP_GIANT();
if ((state & TASK_INTERRUPTIBLE) != 0) {
- if (timeout == 0)
+ if (stimeout == 0)
ret = -sleepq_wait_sig(wchan, 0);
else
ret = -sleepq_timedwait_sig(wchan, 0);
} else {
- if (timeout == 0) {
+ if (stimeout == 0) {
sleepq_wait(wchan, 0);
ret = 0;
} else
@@ -98,18 +116,16 @@ linux_msleep_interruptible(unsigned int ms)
static int
wake_up_task(struct task_struct *task, unsigned int state)
{
- int ret, wakeup_swapper;
+ int ret;
- ret = wakeup_swapper = 0;
+ ret = 0;
sleepq_lock(task);
if ((atomic_read(&task->state) & state) != 0) {
set_task_state(task, TASK_WAKING);
- wakeup_swapper = sleepq_signal(task, SLEEPQ_SLEEP, 0, 0);
+ sleepq_signal(task, SLEEPQ_SLEEP, 0, 0);
ret = 1;
}
sleepq_release(task);
- if (wakeup_swapper)
- kick_proc0();
return (ret);
}
@@ -184,6 +200,65 @@ default_wake_function(wait_queue_t *wq, unsigned int state, int flags,
return (wake_up_task(wq->private, state));
}
+long
+linux_wait_woken(wait_queue_t *wq, unsigned state, long timeout)
+{
+ void *wchan;
+ struct task_struct *task;
+ int ret;
+ int remainder;
+
+ task = current;
+ wchan = wq->private;
+
+ remainder = jiffies + timeout;
+
+ set_task_state(task, state);
+
+ sleepq_lock(wchan);
+ if (!(wq->flags & WQ_FLAG_WOKEN)) {
+ ret = linux_add_to_sleepqueue(wchan, task, "woken",
+ timeout, state);
+ } else {
+ sleepq_release(wchan);
+ ret = 0;
+ }
+
+ set_task_state(task, TASK_RUNNING);
+ wq->flags &= ~WQ_FLAG_WOKEN;
+
+ if (timeout == MAX_SCHEDULE_TIMEOUT)
+ return (MAX_SCHEDULE_TIMEOUT);
+
+ /* range check return value */
+ remainder -= jiffies;
+
+ /* range check return value */
+ if (ret == -ERESTARTSYS && remainder < 1)
+ remainder = 1;
+ else if (remainder < 0)
+ remainder = 0;
+ else if (remainder > timeout)
+ remainder = timeout;
+ return (remainder);
+}
+
+int
+woken_wake_function(wait_queue_t *wq, unsigned int state,
+ int flags __unused, void *key __unused)
+{
+ void *wchan;
+
+ wchan = wq->private;
+
+ sleepq_lock(wchan);
+ wq->flags |= WQ_FLAG_WOKEN;
+ sleepq_signal(wchan, SLEEPQ_SLEEP, 0, 0);
+ sleepq_release(wchan);
+
+ return (1);
+}
+
void
linux_init_wait_entry(wait_queue_t *wq, int flags)
{
@@ -251,7 +326,7 @@ linux_waitqueue_active(wait_queue_head_t *wqh)
}
int
-linux_wait_event_common(wait_queue_head_t *wqh, wait_queue_t *wq, int timeout,
+linux_wait_event_common(wait_queue_head_t *wqh, wait_queue_t *wq, long timeout,
unsigned int state, spinlock_t *lock)
{
struct task_struct *task;
@@ -260,19 +335,8 @@ linux_wait_event_common(wait_queue_head_t *wqh, wait_queue_t *wq, int timeout,
if (lock != NULL)
spin_unlock_irq(lock);
- /* range check timeout */
- if (timeout < 1)
- timeout = 1;
- else if (timeout == MAX_SCHEDULE_TIMEOUT)
- timeout = 0;
-
task = current;
- /*
- * Our wait queue entry is on the stack - make sure it doesn't
- * get swapped out while we sleep.
- */
- PHOLD(task->task_thread->td_proc);
sleepq_lock(task);
if (atomic_read(&task->state) != TASK_WAKING) {
ret = linux_add_to_sleepqueue(task, task, "wevent", timeout,
@@ -281,30 +345,22 @@ linux_wait_event_common(wait_queue_head_t *wqh, wait_queue_t *wq, int timeout,
sleepq_release(task);
ret = 0;
}
- PRELE(task->task_thread->td_proc);
if (lock != NULL)
spin_lock_irq(lock);
return (ret);
}
-int
-linux_schedule_timeout(int timeout)
+long
+linux_schedule_timeout(long timeout)
{
struct task_struct *task;
- int ret;
- int state;
- int remainder;
+ long remainder;
+ int ret, state;
task = current;
- /* range check timeout */
- if (timeout < 1)
- timeout = 1;
- else if (timeout == MAX_SCHEDULE_TIMEOUT)
- timeout = 0;
-
- remainder = ticks + timeout;
+ remainder = jiffies + timeout;
sleepq_lock(task);
state = atomic_read(&task->state);
@@ -317,11 +373,11 @@ linux_schedule_timeout(int timeout)
}
set_task_state(task, TASK_RUNNING);
- if (timeout == 0)
+ if (timeout == MAX_SCHEDULE_TIMEOUT)
return (MAX_SCHEDULE_TIMEOUT);
/* range check return value */
- remainder -= ticks;
+ remainder -= jiffies;
/* range check return value */
if (ret == -ERESTARTSYS && remainder < 1)
@@ -336,13 +392,9 @@ linux_schedule_timeout(int timeout)
static void
wake_up_sleepers(void *wchan)
{
- int wakeup_swapper;
-
sleepq_lock(wchan);
- wakeup_swapper = sleepq_signal(wchan, SLEEPQ_SLEEP, 0, 0);
+ sleepq_signal(wchan, SLEEPQ_SLEEP, 0, 0);
sleepq_release(wchan);
- if (wakeup_swapper)
- kick_proc0();
}
#define bit_to_wchan(word, bit) ((void *)(((uintptr_t)(word) << 6) | (bit)))
@@ -356,18 +408,12 @@ linux_wake_up_bit(void *word, int bit)
int
linux_wait_on_bit_timeout(unsigned long *word, int bit, unsigned int state,
- int timeout)
+ long timeout)
{
struct task_struct *task;
void *wchan;
int ret;
- /* range check timeout */
- if (timeout < 1)
- timeout = 1;
- else if (timeout == MAX_SCHEDULE_TIMEOUT)
- timeout = 0;
-
task = current;
wchan = bit_to_wchan(word, bit);
for (;;) {
diff --git a/sys/compat/linuxkpi/common/src/linux_shrinker.c b/sys/compat/linuxkpi/common/src/linux_shrinker.c
index 52a0472348d8..e06490b92ed1 100644
--- a/sys/compat/linuxkpi/common/src/linux_shrinker.c
+++ b/sys/compat/linuxkpi/common/src/linux_shrinker.c
@@ -32,10 +32,26 @@
#include <linux/compat.h>
#include <linux/shrinker.h>
+#include <linux/slab.h>
TAILQ_HEAD(, shrinker) lkpi_shrinkers = TAILQ_HEAD_INITIALIZER(lkpi_shrinkers);
static struct sx sx_shrinker;
+struct shrinker *
+linuxkpi_shrinker_alloc(unsigned int flags, const char *fmt, ...)
+{
+ struct shrinker *shrinker;
+
+ shrinker = kzalloc(sizeof(*shrinker), GFP_KERNEL);
+ if (shrinker == NULL)
+ return (NULL);
+
+ shrinker->flags = flags | SHRINKER_ALLOCATED;
+ shrinker->seeks = DEFAULT_SEEKS;
+
+ return (shrinker);
+}
+
int
linuxkpi_register_shrinker(struct shrinker *s)
{
@@ -44,6 +60,7 @@ linuxkpi_register_shrinker(struct shrinker *s)
KASSERT(s->count_objects != NULL, ("NULL shrinker"));
KASSERT(s->scan_objects != NULL, ("NULL shrinker"));
sx_xlock(&sx_shrinker);
+ s->flags |= SHRINKER_REGISTERED;
TAILQ_INSERT_TAIL(&lkpi_shrinkers, s, next);
sx_xunlock(&sx_shrinker);
return (0);
@@ -55,10 +72,21 @@ linuxkpi_unregister_shrinker(struct shrinker *s)
sx_xlock(&sx_shrinker);
TAILQ_REMOVE(&lkpi_shrinkers, s, next);
+ s->flags &= ~SHRINKER_REGISTERED;
sx_xunlock(&sx_shrinker);
}
void
+linuxkpi_shrinker_free(struct shrinker *shrinker)
+{
+
+ if (shrinker->flags & SHRINKER_REGISTERED)
+ unregister_shrinker(shrinker);
+
+ kfree(shrinker);
+}
+
+void
linuxkpi_synchronize_shrinkers(void)
{
@@ -92,7 +120,7 @@ shrinker_shrink(struct shrinker *s)
}
static void
-linuxkpi_vm_lowmem(void *arg __unused)
+linuxkpi_vm_lowmem(void *arg __unused, int flags __unused)
{
struct shrinker *s;
diff --git a/sys/compat/linuxkpi/common/src/linux_simple_attr.c b/sys/compat/linuxkpi/common/src/linux_simple_attr.c
index 1bdacd7c1491..8cc9ec7ecbc9 100644
--- a/sys/compat/linuxkpi/common/src/linux_simple_attr.c
+++ b/sys/compat/linuxkpi/common/src/linux_simple_attr.c
@@ -138,12 +138,13 @@ unlock:
}
/*
- * simple_attr_write: write contents of buffer into simple attribute file
+ * simple_attr_write_common: write contents of buffer into simple attribute file
*
* @filp: file pointer
* @buf: kernel space buffer
* @write_size: number bytes to be transferred
* @ppos: starting pointer position for transfer
+ * @is_signed: signedness of data in @buf
*
* The simple_attr structure is stored in filp->private_data.
* Convert the @buf string to unsigned long long.
@@ -153,8 +154,9 @@ unlock:
* On success, number of bytes written to simple attr
* On failure, negative signed ERRNO
*/
-ssize_t
-simple_attr_write(struct file *filp, const char *buf, size_t write_size, loff_t *ppos)
+static ssize_t
+simple_attr_write_common(struct file *filp, const char *buf, size_t write_size,
+ loff_t *ppos, bool is_signed)
{
struct simple_attr *sattr;
unsigned long long data;
@@ -172,7 +174,10 @@ simple_attr_write(struct file *filp, const char *buf, size_t write_size, loff_t
mutex_lock(&sattr->mutex);
- ret = kstrtoull(buf + *ppos, 0, &data);
+ if (is_signed)
+ ret = kstrtoll(buf + *ppos, 0, &data);
+ else
+ ret = kstrtoull(buf + *ppos, 0, &data);
if (ret)
goto unlock;
@@ -186,3 +191,17 @@ unlock:
mutex_unlock(&sattr->mutex);
return (ret);
}
+
+ssize_t
+simple_attr_write(struct file *filp, const char *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)
+{
+ return (simple_attr_write_common(filp, buf, write_size, ppos, true));
+}
diff --git a/sys/compat/linuxkpi/common/src/linux_skbuff.c b/sys/compat/linuxkpi/common/src/linux_skbuff.c
index 0522d3fdff41..abfb642ba708 100644
--- a/sys/compat/linuxkpi/common/src/linux_skbuff.c
+++ b/sys/compat/linuxkpi/common/src/linux_skbuff.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2020-2022 The FreeBSD Foundation
+ * Copyright (c) 2020-2025 The FreeBSD Foundation
* Copyright (c) 2021-2022 Bjoern A. Zeeb
*
* This software was developed by Björn Zeeb under sponsorship from
@@ -42,6 +42,8 @@
#include <sys/malloc.h>
#include <sys/sysctl.h>
+#include <vm/uma.h>
+
#ifdef DDB
#include <ddb/ddb.h>
#endif
@@ -63,68 +65,82 @@ SYSCTL_INT(_compat_linuxkpi_skb, OID_AUTO, debug, CTLFLAG_RWTUN,
&linuxkpi_debug_skb, 0, "SKB debug level");
#endif
-#ifdef __LP64__
+static uma_zone_t skbzone;
+
+#define SKB_DMA32_MALLOC
+#ifdef SKB_DMA32_MALLOC
/*
* Realtek wireless drivers (e.g., rtw88) require 32bit DMA in a single segment.
* busdma(9) has a hard time providing this currently for 3-ish pages at large
* quantities (see lkpi_pci_nseg1_fail in linux_pci.c).
* Work around this for now by allowing a tunable to enforce physical addresses
- * allocation limits on 64bit platforms using "old-school" contigmalloc(9) to
- * avoid bouncing.
+ * allocation limits using "old-school" contigmalloc(9) to avoid bouncing.
+ * Note: with the malloc/contigmalloc + kmalloc changes also providing physical
+ * contiguous memory, and the nseg=1 limit for bouncing we should in theory be
+ * fine now and not need any of this anymore, however busdma still has troubles
+ * boncing three contiguous pages so for now this stays.
*/
static int linuxkpi_skb_memlimit;
SYSCTL_INT(_compat_linuxkpi_skb, OID_AUTO, mem_limit, CTLFLAG_RDTUN,
&linuxkpi_skb_memlimit, 0, "SKB memory limit: 0=no limit, "
"1=32bit, 2=36bit, other=undef (currently 32bit)");
-#endif
static MALLOC_DEFINE(M_LKPISKB, "lkpiskb", "Linux KPI skbuff compat");
+#endif
struct sk_buff *
linuxkpi_alloc_skb(size_t size, gfp_t gfp)
{
struct sk_buff *skb;
+ void *p;
size_t len;
- len = sizeof(*skb) + size + sizeof(struct skb_shared_info);
+ skb = uma_zalloc(skbzone, linux_check_m_flags(gfp) | M_ZERO);
+ if (skb == NULL)
+ return (NULL);
+
+ skb->prev = skb->next = skb;
+ skb->truesize = size;
+ skb->shinfo = (struct skb_shared_info *)(skb + 1);
+
+ if (size == 0)
+ return (skb);
+
+ len = size;
+#ifdef SKB_DMA32_MALLOC
/*
* Using our own type here not backing my kmalloc.
* We assume no one calls kfree directly on the skb.
*/
-#ifdef __LP64__
- if (__predict_true(linuxkpi_skb_memlimit == 0)) {
- skb = malloc(len, M_LKPISKB, linux_check_m_flags(gfp) | M_ZERO);
- } else {
+ if (__predict_false(linuxkpi_skb_memlimit != 0)) {
vm_paddr_t high;
switch (linuxkpi_skb_memlimit) {
+#ifdef __LP64__
case 2:
high = (0xfffffffff); /* 1<<36 really. */
break;
+#endif
case 1:
default:
high = (0xffffffff); /* 1<<32 really. */
break;
}
len = roundup_pow_of_two(len);
- skb = contigmalloc(len, M_LKPISKB,
+ p = contigmalloc(len, M_LKPISKB,
linux_check_m_flags(gfp) | M_ZERO, 0, high, PAGE_SIZE, 0);
- }
-#else
- skb = malloc(len, M_LKPISKB, linux_check_m_flags(gfp) | M_ZERO);
+ } else
#endif
- if (skb == NULL)
- return (skb);
- skb->_alloc_len = len;
- skb->truesize = size;
+ p = __kmalloc(len, linux_check_m_flags(gfp) | M_ZERO);
+ if (p == NULL) {
+ uma_zfree(skbzone, skb);
+ return (NULL);
+ }
- skb->head = skb->data = skb->tail = (uint8_t *)(skb+1);
+ skb->head = skb->data = (uint8_t *)p;
+ skb_reset_tail_pointer(skb);
skb->end = skb->head + size;
- skb->prev = skb->next = skb;
-
- skb->shinfo = (struct skb_shared_info *)(skb->end);
-
SKB_TRACE_FMT(skb, "data %p size %zu", (skb) ? skb->data : NULL, size);
return (skb);
}
@@ -162,14 +178,14 @@ linuxkpi_build_skb(void *data, size_t fragsz)
skb->_flags |= _SKB_FLAGS_SKBEXTFRAG;
skb->truesize = fragsz;
skb->head = skb->data = data;
- skb_reset_tail_pointer(skb); /* XXX is that correct? */
- skb->end = (void *)((uintptr_t)skb->head + fragsz);
+ skb_reset_tail_pointer(skb);
+ skb->end = skb->head + fragsz;
return (skb);
}
struct sk_buff *
-linuxkpi_skb_copy(struct sk_buff *skb, gfp_t gfp)
+linuxkpi_skb_copy(const struct sk_buff *skb, gfp_t gfp)
{
struct sk_buff *new;
struct skb_shared_info *shinfo;
@@ -256,17 +272,34 @@ linuxkpi_kfree_skb(struct sk_buff *skb)
p = skb->head;
skb_free_frag(p);
+ skb->head = NULL;
}
-#ifdef __LP64__
- if (__predict_true(linuxkpi_skb_memlimit == 0))
- free(skb, M_LKPISKB);
+#ifdef SKB_DMA32_MALLOC
+ if (__predict_false(linuxkpi_skb_memlimit != 0))
+ free(skb->head, M_LKPISKB);
else
- contigfree(skb, skb->_alloc_len, M_LKPISKB);
-#else
- free(skb, M_LKPISKB);
#endif
+ kfree(skb->head);
+ uma_zfree(skbzone, skb);
+}
+
+static void
+lkpi_skbuff_init(void *arg __unused)
+{
+ skbzone = uma_zcreate("skbuff",
+ sizeof(struct sk_buff) + sizeof(struct skb_shared_info),
+ NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
+ /* Do we need to apply limits? */
+}
+SYSINIT(linuxkpi_skbuff, SI_SUB_DRIVERS, SI_ORDER_FIRST, lkpi_skbuff_init, NULL);
+
+static void
+lkpi_skbuff_destroy(void *arg __unused)
+{
+ uma_zdestroy(skbzone);
}
+SYSUNINIT(linuxkpi_skbuff, SI_SUB_DRIVERS, SI_ORDER_SECOND, lkpi_skbuff_destroy, NULL);
#ifdef DDB
DB_SHOW_COMMAND(skb, db_show_skb)
@@ -284,9 +317,8 @@ DB_SHOW_COMMAND(skb, db_show_skb)
db_printf("skb %p\n", skb);
db_printf("\tnext %p prev %p\n", skb->next, skb->prev);
db_printf("\tlist %p\n", &skb->list);
- db_printf("\t_alloc_len %u len %u data_len %u truesize %u mac_len %u\n",
- skb->_alloc_len, skb->len, skb->data_len, skb->truesize,
- skb->mac_len);
+ db_printf("\tlen %u data_len %u truesize %u mac_len %u\n",
+ skb->len, skb->data_len, skb->truesize, skb->mac_len);
db_printf("\tcsum %#06x l3hdroff %u l4hdroff %u priority %u qmap %u\n",
skb->csum, skb->l3hdroff, skb->l4hdroff, skb->priority, skb->qmap);
db_printf("\tpkt_type %d dev %p sk %p\n",
diff --git a/sys/compat/linuxkpi/common/src/linux_slab.c b/sys/compat/linuxkpi/common/src/linux_slab.c
index 68117d1c9fa7..3d75ca480661 100644
--- a/sys/compat/linuxkpi/common/src/linux_slab.c
+++ b/sys/compat/linuxkpi/common/src/linux_slab.c
@@ -1,6 +1,10 @@
/*-
* Copyright (c) 2017 Mellanox Technologies, Ltd.
* All rights reserved.
+ * Copyright (c) 2024-2025 The FreeBSD Foundation
+ *
+ * Portions of this software were developed by Björn Zeeb
+ * 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
@@ -207,6 +211,68 @@ linux_kmem_cache_destroy(struct linux_kmem_cache *c)
free(c, M_KMALLOC);
}
+void *
+lkpi___kmalloc_node(size_t size, gfp_t flags, int node)
+{
+ if (size <= PAGE_SIZE)
+ return (malloc_domainset(size, M_KMALLOC,
+ linux_get_vm_domain_set(node), linux_check_m_flags(flags)));
+ else
+ return (contigmalloc_domainset(size, M_KMALLOC,
+ linux_get_vm_domain_set(node), linux_check_m_flags(flags),
+ 0, -1UL, PAGE_SIZE, 0));
+}
+
+void *
+lkpi___kmalloc(size_t size, gfp_t flags)
+{
+ size_t _s;
+
+ /* sizeof(struct llist_node) is used for kfree_async(). */
+ _s = MAX(size, sizeof(struct llist_node));
+
+ if (_s <= PAGE_SIZE)
+ return (malloc(_s, M_KMALLOC, linux_check_m_flags(flags)));
+ else
+ return (contigmalloc(_s, M_KMALLOC, linux_check_m_flags(flags),
+ 0, -1UL, PAGE_SIZE, 0));
+}
+
+void *
+lkpi_krealloc(void *ptr, size_t size, gfp_t flags)
+{
+ void *nptr;
+ size_t osize;
+
+ /*
+ * First handle invariants based on function arguments.
+ */
+ if (ptr == NULL)
+ return (kmalloc(size, flags));
+
+ osize = ksize(ptr);
+ if (size <= osize)
+ return (ptr);
+
+ /*
+ * We know the new size > original size. realloc(9) does not (and cannot)
+ * know about our requirements for physically contiguous memory, so we can
+ * only call it for sizes up to and including PAGE_SIZE, and otherwise have
+ * to replicate its functionality using kmalloc to get the contigmalloc(9)
+ * backing.
+ */
+ if (size <= PAGE_SIZE)
+ return (realloc(ptr, size, M_KMALLOC, linux_check_m_flags(flags)));
+
+ nptr = kmalloc(size, flags);
+ if (nptr == NULL)
+ return (NULL);
+
+ memcpy(nptr, ptr, osize);
+ kfree(ptr);
+ return (nptr);
+}
+
struct lkpi_kmalloc_ctx {
size_t size;
gfp_t flags;
@@ -241,7 +307,7 @@ linux_kfree_async_fn(void *context, int pending)
static struct task linux_kfree_async_task =
TASK_INITIALIZER(0, linux_kfree_async_fn, &linux_kfree_async_task);
-void
+static void
linux_kfree_async(void *addr)
{
if (addr == NULL)
@@ -249,3 +315,16 @@ linux_kfree_async(void *addr)
llist_add(addr, &linux_kfree_async_list);
taskqueue_enqueue(linux_irq_work_tq, &linux_kfree_async_task);
}
+
+void
+lkpi_kfree(const void *ptr)
+{
+ if (ZERO_OR_NULL_PTR(ptr))
+ return;
+
+ if (curthread->td_critnest != 0)
+ linux_kfree_async(__DECONST(void *, ptr));
+ else
+ free(__DECONST(void *, ptr), M_KMALLOC);
+}
+
diff --git a/sys/compat/linuxkpi/common/src/linux_work.c b/sys/compat/linuxkpi/common/src/linux_work.c
index 939bdbbc1434..b1975d16025e 100644
--- a/sys/compat/linuxkpi/common/src/linux_work.c
+++ b/sys/compat/linuxkpi/common/src/linux_work.c
@@ -212,7 +212,7 @@ linux_flush_rcu_work(struct rcu_work *rwork)
*/
bool
linux_queue_delayed_work_on(int cpu, struct workqueue_struct *wq,
- struct delayed_work *dwork, unsigned delay)
+ struct delayed_work *dwork, unsigned long delay)
{
static const uint8_t states[WORK_ST_MAX] __aligned(8) = {
[WORK_ST_IDLE] = WORK_ST_TIMER, /* start timeout */
@@ -226,6 +226,13 @@ linux_queue_delayed_work_on(int cpu, struct workqueue_struct *wq,
if (atomic_read(&wq->draining) != 0)
return (!work_pending(&dwork->work));
+ /*
+ * Clamp the delay to a valid ticks value, some consumers pass
+ * MAX_SCHEDULE_TIMEOUT.
+ */
+ if (delay > INT_MAX)
+ delay = INT_MAX;
+
mtx_lock(&dwork->timer.mtx);
switch (linux_update_state(&dwork->work.state, states)) {
case WORK_ST_EXEC:
diff --git a/sys/compat/linuxkpi/common/src/linux_xarray.c b/sys/compat/linuxkpi/common/src/linux_xarray.c
index 44900666242f..3f07f6d7c59f 100644
--- a/sys/compat/linuxkpi/common/src/linux_xarray.c
+++ b/sys/compat/linuxkpi/common/src/linux_xarray.c
@@ -52,7 +52,7 @@ __xa_erase(struct xarray *xa, uint32_t index)
XA_ASSERT_LOCKED(xa);
- retval = radix_tree_delete(&xa->root, index);
+ retval = radix_tree_delete(&xa->xa_head, index);
if (retval == NULL_VALUE)
retval = NULL;
@@ -81,7 +81,7 @@ xa_load(struct xarray *xa, uint32_t index)
void *retval;
xa_lock(xa);
- retval = radix_tree_lookup(&xa->root, index);
+ retval = radix_tree_lookup(&xa->xa_head, index);
xa_unlock(xa);
if (retval == NULL_VALUE)
@@ -122,16 +122,16 @@ __xa_alloc(struct xarray *xa, uint32_t *pindex, void *ptr, uint32_t mask, gfp_t
XA_ASSERT_LOCKED(xa);
/* mask should allow to allocate at least one item */
- MPASS(mask > ((xa->flags & XA_FLAGS_ALLOC1) != 0 ? 1 : 0));
+ MPASS(mask > ((xa->xa_flags & XA_FLAGS_ALLOC1) != 0 ? 1 : 0));
/* mask can be any power of two value minus one */
MPASS((mask & (mask + 1)) == 0);
- *pindex = (xa->flags & XA_FLAGS_ALLOC1) != 0 ? 1 : 0;
+ *pindex = (xa->xa_flags & XA_FLAGS_ALLOC1) != 0 ? 1 : 0;
if (ptr == NULL)
ptr = NULL_VALUE;
retry:
- retval = radix_tree_insert(&xa->root, *pindex, ptr);
+ retval = radix_tree_insert(&xa->xa_head, *pindex, ptr);
switch (retval) {
case -EEXIST:
@@ -184,16 +184,16 @@ __xa_alloc_cyclic(struct xarray *xa, uint32_t *pindex, void *ptr, uint32_t mask,
XA_ASSERT_LOCKED(xa);
/* mask should allow to allocate at least one item */
- MPASS(mask > ((xa->flags & XA_FLAGS_ALLOC1) != 0 ? 1 : 0));
+ MPASS(mask > ((xa->xa_flags & XA_FLAGS_ALLOC1) != 0 ? 1 : 0));
/* mask can be any power of two value minus one */
MPASS((mask & (mask + 1)) == 0);
- *pnext_index = (xa->flags & XA_FLAGS_ALLOC1) != 0 ? 1 : 0;
+ *pnext_index = (xa->xa_flags & XA_FLAGS_ALLOC1) != 0 ? 1 : 0;
if (ptr == NULL)
ptr = NULL_VALUE;
retry:
- retval = radix_tree_insert(&xa->root, *pnext_index, ptr);
+ retval = radix_tree_insert(&xa->xa_head, *pnext_index, ptr);
switch (retval) {
case -EEXIST:
@@ -203,7 +203,7 @@ retry:
}
(*pnext_index)++;
(*pnext_index) &= mask;
- if (*pnext_index == 0 && (xa->flags & XA_FLAGS_ALLOC1) != 0)
+ if (*pnext_index == 0 && (xa->xa_flags & XA_FLAGS_ALLOC1) != 0)
(*pnext_index)++;
goto retry;
case -ENOMEM:
@@ -233,6 +233,19 @@ xa_alloc_cyclic(struct xarray *xa, uint32_t *pindex, void *ptr, uint32_t mask,
return (retval);
}
+int
+xa_alloc_cyclic_irq(struct xarray *xa, uint32_t *pindex, void *ptr,
+ uint32_t mask, uint32_t *pnext_index, gfp_t gfp)
+{
+ int retval;
+
+ xa_lock_irq(xa);
+ retval = __xa_alloc_cyclic(xa, pindex, ptr, mask, pnext_index, gfp);
+ xa_unlock_irq(xa);
+
+ return (retval);
+}
+
/*
* This function tries to insert an element at the given index. The
* "gfp" argument basically decides of this function can sleep or not
@@ -249,7 +262,7 @@ __xa_insert(struct xarray *xa, uint32_t index, void *ptr, gfp_t gfp)
if (ptr == NULL)
ptr = NULL_VALUE;
retry:
- retval = radix_tree_insert(&xa->root, index, ptr);
+ retval = radix_tree_insert(&xa->xa_head, index, ptr);
switch (retval) {
case -ENOMEM:
@@ -293,7 +306,7 @@ __xa_store(struct xarray *xa, uint32_t index, void *ptr, gfp_t gfp)
if (ptr == NULL)
ptr = NULL_VALUE;
retry:
- retval = radix_tree_store(&xa->root, index, &ptr);
+ retval = radix_tree_store(&xa->xa_head, index, &ptr);
switch (retval) {
case 0:
@@ -334,9 +347,9 @@ xa_init_flags(struct xarray *xa, uint32_t flags)
{
memset(xa, 0, sizeof(*xa));
- mtx_init(&xa->mtx, "lkpi-xarray", NULL, MTX_DEF | MTX_RECURSE);
- xa->root.gfp_mask = GFP_NOWAIT;
- xa->flags = flags;
+ mtx_init(&xa->xa_lock, "lkpi-xarray", NULL, MTX_DEF | MTX_RECURSE);
+ xa->xa_head.gfp_mask = GFP_NOWAIT;
+ xa->xa_flags = flags;
}
/*
@@ -349,9 +362,19 @@ xa_destroy(struct xarray *xa)
struct radix_tree_iter iter;
void **ppslot;
- radix_tree_for_each_slot(ppslot, &xa->root, &iter, 0)
- radix_tree_iter_delete(&xa->root, &iter, ppslot);
- mtx_destroy(&xa->mtx);
+ xa_lock(xa);
+ radix_tree_for_each_slot(ppslot, &xa->xa_head, &iter, 0)
+ radix_tree_iter_delete(&xa->xa_head, &iter, ppslot);
+ xa_unlock(xa);
+
+ /*
+ * The mutex initialized in `xa_init_flags()` is not destroyed here on
+ * purpose. The reason is that on Linux, the xarray remains usable
+ * after a call to `xa_destroy()`. For instance the i915 DRM driver
+ * relies on that during the initialixation of its GuC. Basically,
+ * `xa_destroy()` "resets" the structure to zero but doesn't really
+ * destroy it.
+ */
}
/*
@@ -366,7 +389,7 @@ __xa_empty(struct xarray *xa)
XA_ASSERT_LOCKED(xa);
- return (!radix_tree_iter_find(&xa->root, &iter, &temp));
+ return (!radix_tree_iter_find(&xa->xa_head, &iter, &temp));
}
bool
@@ -403,7 +426,7 @@ __xa_next(struct xarray *xa, unsigned long *pindex, bool not_first)
return (NULL);
}
- found = radix_tree_iter_find(&xa->root, &iter, &ppslot);
+ found = radix_tree_iter_find(&xa->xa_head, &iter, &ppslot);
if (likely(found)) {
retval = *ppslot;
if (retval == NULL_VALUE)