Merge Official Source

Signed-off-by: Tianling Shen <cnsztl@immortalwrt.org>
This commit is contained in:
Tianling Shen
2026-07-22 11:02:55 +08:00
224 changed files with 9666 additions and 5516 deletions
+9
View File
@@ -83,6 +83,15 @@ menu "Global build settings"
--allow-untrusted when installing self-compiled packages to a
firmware compiled by the same buildhost as public key matches.
config SIGN_FIRMWARE
bool "Cryptographically sign firmware images"
default n if BUILDBOT
default y
help
Append a signature to firmware images so that sysupgrade can verify
their authenticity before flashing. OpenWrt's build infrastructure
signs images with temporary keys; disabling this drops the signature.
comment "General build options"
config TESTING_KERNEL
+4 -4
View File
@@ -91,12 +91,12 @@ metadata_json = \
define Build/append-metadata
$(if $(SUPPORTED_DEVICES),-echo $(call metadata_json) | fwtool -I - $@)
sha256sum "$@" | cut -d" " -f1 > "$@.sha256sum"
[ ! -s "$(BUILD_KEY)" -o ! -s "$(BUILD_KEY).ucert" -o ! -s "$@" ] || { \
$(if $(CONFIG_SIGN_FIRMWARE),[ ! -s "$(BUILD_KEY)" -o ! -s "$(BUILD_KEY).ucert" -o ! -s "$@" ] || { \
cp "$(BUILD_KEY).ucert" "$@.ucert" ;\
usign -S -m "$@" -s "$(BUILD_KEY)" -x "$@.sig" ;\
ucert -A -c "$@.ucert" -x "$@.sig" ;\
fwtool -S "$@.ucert" "$@" ;\
}
})
endef
metadata_gl_json = \
@@ -122,12 +122,12 @@ metadata_gl_json = \
define Build/append-gl-metadata
$(if $(SUPPORTED_DEVICES),-echo $(call metadata_gl_json,$(SUPPORTED_DEVICES)) | fwtool -I - $@)
sha256sum "$@" | cut -d" " -f1 > "$@.sha256sum"
[ ! -s "$(BUILD_KEY)" -o ! -s "$(BUILD_KEY).ucert" -o ! -s "$@" ] || { \
$(if $(CONFIG_SIGN_FIRMWARE),[ ! -s "$(BUILD_KEY)" -o ! -s "$(BUILD_KEY).ucert" -o ! -s "$@" ] || { \
cp "$(BUILD_KEY).ucert" "$@.ucert" ;\
usign -S -m "$@" -s "$(BUILD_KEY)" -x "$@.sig" ;\
ucert -A -c "$@.ucert" -x "$@.sig" ;\
fwtool -S "$@.ucert" "$@" ;\
}
})
endef
define Build/append-teltonika-metadata
+5
View File
@@ -102,6 +102,7 @@ KERNEL_MAKE = $(MAKE) $(KERNEL_MAKEOPTS)
KERNEL_MAKE_FLAGS = \
KCFLAGS="$(call iremap,$(BUILD_DIR),$(notdir $(BUILD_DIR))) $(filter-out -fno-plt,$(call qstrip,$(CONFIG_EXTRA_OPTIMIZATION))) $(call qstrip,$(CONFIG_KERNEL_CFLAGS))" \
KAFLAGS="$(call iremap,$(BUILD_DIR),$(notdir $(BUILD_DIR)))" \
HOSTCFLAGS="$(HOST_CFLAGS) -Wall -Wmissing-prototypes -Wstrict-prototypes" \
CROSS_COMPILE="$(KERNEL_CROSS)" \
ARCH="$(LINUX_KARCH)" \
@@ -121,6 +122,10 @@ ifneq (,$(KERNEL_CC))
KERNEL_MAKE_FLAGS += CC="$(KERNEL_CC)"
endif
ifeq ($(HOST_OS),Darwin)
KERNEL_MAKE_FLAGS += MACOSX_DEPLOYMENT_TARGET="$(shell sw_vers -productVersion)"
endif
KERNEL_NOSTDINC_FLAGS = \
-nostdinc $(if $(DUMP),, -isystem $(shell $(TARGET_CC) -print-file-name=include))
@@ -278,7 +278,7 @@ index 00000000000..e1fda15ba37
+ reg = <0x0 0x1fbf0200 0x0 0xc0>;
+
+ an7583_pinctrl: pinctrl {
+ compatible = "airoha,en7583-pinctrl";
+ compatible = "airoha,an7583-pinctrl";
+
+ interrupt-parent = <&gic>;
+ interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
@@ -0,0 +1,23 @@
#
# Copyright (C) 2025 OpenWrt.org
#
[ -e /etc/config/ubootenv ] && exit 0
touch /etc/config/ubootenv
. /lib/uboot-envtools.sh
. /lib/functions.sh
board=$(board_name)
case "$board" in
nokia,xg-040g-mf)
ubootenv_add_mtd "u-boot-env" "0x1c000" "0x4000" "0x4000"
;;
esac
config_load ubootenv
config_foreach ubootenv_add_app_config
exit 0
@@ -32,6 +32,7 @@ allnet,all0256n-4m|\
allnet,all0256n-8m|\
allnet,all5002|\
asiarf,ap7621-004-v3|\
comfast,cf-ew84|\
huasifei,shf283|\
jdcloud,re-cp-02|\
plasmacloud,pax1800-lite|\
@@ -21,6 +21,7 @@ zyxel,gs1900-8-a1|\
zyxel,gs1900-8hp-a1|\
zyxel,gs1900-8hp-b1|\
zyxel,gs1900-10hp-a1|\
zyxel,gs1900-10hp-b1|\
zyxel,gs1900-16-a1|\
zyxel,gs1900-24-a1|\
zyxel,gs1900-24e-a1|\
@@ -33,13 +33,11 @@ endef
define Package/rtl826x-firmware
$(call Package/rtl826x-firmware/Default)
TITLE:=Realtek RTL8251L/RTL8261BE/RTL8261N/RTL8254B/RTL8264/RTL8264B firmware
VERSION:=20221115
endef
define Package/rtl826x-lp-firmware
$(call Package/rtl826x-firmware/Default)
TITLE:=Realtek RTL8251L/RTL8261BE/RTL8261N/RTL8254B/RTL8264/RTL8264B low-power firmware
VERSION:=20240729
PROVIDES:=rtl826x-firmware
CONFLICTS:=rtl826x-firmware
endef
-95
View File
@@ -770,101 +770,6 @@ endef
$(eval $(call KernelPackage,crypto-michael-mic))
define KernelPackage/crypto-misc
TITLE:=Other CryptoAPI modules
DEPENDS:=+kmod-crypto-xts +kmod-crypto-user
KCONFIG:= \
CONFIG_CRYPTO_USER_API_ENABLE_OBSOLETE=y \
CONFIG_CRYPTO_CAMELLIA_X86_64 \
CONFIG_CRYPTO_BLOWFISH_X86_64 \
CONFIG_CRYPTO_TWOFISH_X86_64 \
CONFIG_CRYPTO_TWOFISH_X86_64_3WAY \
CONFIG_CRYPTO_SERPENT_SSE2_X86_64 \
CONFIG_CRYPTO_CAMELLIA_AESNI_AVX_X86_64 \
CONFIG_CRYPTO_CAST5_AVX_X86_64 \
CONFIG_CRYPTO_CAST6_AVX_X86_64 \
CONFIG_CRYPTO_TWOFISH_AVX_X86_64 \
CONFIG_CRYPTO_SERPENT_AVX_X86_64 \
CONFIG_CRYPTO_CAMELLIA_AESNI_AVX2_X86_64 \
CONFIG_CRYPTO_SERPENT_AVX2_X86_64 \
CONFIG_CRYPTO_SERPENT_SSE2_586 \
CONFIG_CRYPTO_ANUBIS \
CONFIG_CRYPTO_BLOWFISH \
CONFIG_CRYPTO_CAMELLIA \
CONFIG_CRYPTO_CAST5 \
CONFIG_CRYPTO_CAST6 \
CONFIG_CRYPTO_KHAZAD \
CONFIG_CRYPTO_SERPENT \
CONFIG_CRYPTO_TEA \
CONFIG_CRYPTO_TWOFISH \
CONFIG_CRYPTO_TWOFISH_COMMON \
CONFIG_CRYPTO_TWOFISH_586 \
CONFIG_CRYPTO_WP512
FILES:= \
$(LINUX_DIR)/crypto/anubis.ko \
$(LINUX_DIR)/crypto/camellia_generic.ko \
$(LINUX_DIR)/crypto/cast_common.ko \
$(LINUX_DIR)/crypto/cast5_generic.ko \
$(LINUX_DIR)/crypto/cast6_generic.ko \
$(LINUX_DIR)/crypto/khazad.ko \
$(LINUX_DIR)/crypto/tea.ko \
$(LINUX_DIR)/crypto/twofish_common.ko \
$(LINUX_DIR)/crypto/wp512.ko \
$(LINUX_DIR)/crypto/twofish_generic.ko \
$(LINUX_DIR)/crypto/blowfish_common.ko \
$(LINUX_DIR)/crypto/blowfish_generic.ko \
$(LINUX_DIR)/crypto/serpent_generic.ko
AUTOLOAD:=$(call AutoLoad,10,anubis camellia_generic cast_common \
cast5_generic cast6_generic khazad tea twofish_common \
wp512 blowfish_common serpent_generic)
ifndef CONFIG_TARGET_x86
AUTOLOAD+= $(call AutoLoad,10,twofish_generic blowfish_generic)
endif
$(call AddDepends/crypto)
endef
ifndef CONFIG_TARGET_x86_64
define KernelPackage/crypto-misc/x86
FILES+= \
$(LINUX_DIR)/arch/x86/crypto/twofish-i586.ko \
$(LINUX_DIR)/arch/x86/crypto/serpent-sse2-i586.ko \
$(LINUX_DIR)/crypto/cryptd.ko@lt6.18 \
$(LINUX_DIR)/crypto/crypto_simd.ko@lt6.18
AUTOLOAD+= $(call AutoLoad,10,cryptd@lt6.18 \
serpent-sse2-i586 twofish-i586 blowfish_generic)
endef
endif
ifndef CONFIG_TARGET_uml
define KernelPackage/crypto-misc/x86_64
FILES+= \
$(LINUX_DIR)/arch/x86/crypto/camellia-x86_64.ko \
$(LINUX_DIR)/arch/x86/crypto/blowfish-x86_64.ko \
$(LINUX_DIR)/arch/x86/crypto/twofish-x86_64.ko \
$(LINUX_DIR)/arch/x86/crypto/twofish-x86_64-3way.ko \
$(LINUX_DIR)/arch/x86/crypto/serpent-sse2-x86_64.ko \
$(LINUX_DIR)/arch/x86/crypto/camellia-aesni-avx-x86_64.ko \
$(LINUX_DIR)/arch/x86/crypto/cast5-avx-x86_64.ko \
$(LINUX_DIR)/arch/x86/crypto/cast6-avx-x86_64.ko \
$(LINUX_DIR)/arch/x86/crypto/twofish-avx-x86_64.ko \
$(LINUX_DIR)/arch/x86/crypto/serpent-avx-x86_64.ko \
$(LINUX_DIR)/arch/x86/crypto/camellia-aesni-avx2.ko \
$(LINUX_DIR)/arch/x86/crypto/serpent-avx2.ko
AUTOLOAD+= $(call AutoLoad,10,camellia-x86_64 \
camellia-aesni-avx-x86_64 camellia-aesni-avx2 cast5-avx-x86_64 \
cast6-avx-x86_64 twofish-x86_64 twofish-x86_64-3way \
twofish-avx-x86_64 blowfish-x86_64 serpent-avx-x86_64 serpent-avx2)
endef
endif
ifdef KernelPackage/crypto-misc/$(ARCH)
KernelPackage/crypto-misc/$(CRYPTO_TARGET)=\
$(KernelPackage/crypto-misc/$(ARCH))
endif
$(eval $(call KernelPackage,crypto-misc))
define KernelPackage/crypto-null
TITLE:=Null CryptoAPI module
KCONFIG:=CONFIG_CRYPTO_NULL
+2 -2
View File
@@ -1418,7 +1418,7 @@ define KernelPackage/e1000
CONFIG_E1000_DISABLE_PACKET_SPLIT=n \
CONFIG_E1000_NAPI=y
FILES:=$(LINUX_DIR)/drivers/net/ethernet/intel/e1000/e1000.ko
AUTOLOAD:=$(call AutoLoad,35,e1000)
AUTOLOAD:=$(call AutoLoad,35,e1000,1)
endef
define KernelPackage/e1000/description
@@ -1872,7 +1872,7 @@ define KernelPackage/vmxnet3
DEPENDS:=@PCI_SUPPORT
KCONFIG:=CONFIG_VMXNET3
FILES:=$(LINUX_DIR)/drivers/net/vmxnet3/vmxnet3.ko
AUTOLOAD:=$(call AutoLoad,35,vmxnet3)
AUTOLOAD:=$(call AutoLoad,35,vmxnet3,1)
endef
define KernelPackage/vmxnet3/description
+104
View File
@@ -935,6 +935,93 @@ endef
$(eval $(call KernelPackage,echo))
define KernelPackage/tee
SUBMENU:=$(OTHER_MENU)
TITLE:=Trusted Execution Environment (TEE) support
DEPENDS:=+kmod-dma-buf
KCONFIG:=CONFIG_TEE
FILES:=$(LINUX_DIR)/drivers/tee/tee.ko
AUTOLOAD:=$(call AutoLoad,20,tee,1)
endef
define KernelPackage/tee/description
Generic Trusted Execution Environment subsystem, the interface used to
communicate with a trusted OS running in a secure environment.
endef
$(eval $(call KernelPackage,tee))
define KernelPackage/optee
SUBMENU:=$(OTHER_MENU)
TITLE:=OP-TEE driver
DEPENDS:=+kmod-i2c-core +kmod-tee
KCONFIG:= \
CONFIG_OPTEE \
CONFIG_OPTEE_INSECURE_LOAD_IMAGE=n
FILES:=$(LINUX_DIR)/drivers/tee/optee/optee.ko
AUTOLOAD:=$(call AutoLoad,21,optee,1)
endef
define KernelPackage/optee/description
Driver for OP-TEE, the Open Portable Trusted Execution Environment running
in the Arm secure world, reached over the SMCCC or FF-A transport.
endef
$(eval $(call KernelPackage,optee))
define KernelPackage/optee-rng
SUBMENU:=$(OTHER_MENU)
TITLE:=OP-TEE based Random Number Generator
DEPENDS:=+kmod-optee +kmod-random-core
KCONFIG:=CONFIG_HW_RANDOM_OPTEE
FILES:=$(LINUX_DIR)/drivers/char/hw_random/optee-rng.ko
AUTOLOAD:=$(call AutoLoad,22,optee-rng,1)
endef
define KernelPackage/optee-rng/description
Exposes the random number generator provided by OP-TEE through the kernel
hwrng interface.
endef
$(eval $(call KernelPackage,optee-rng))
define KernelPackage/scmi-transport-optee
SUBMENU:=$(OTHER_MENU)
TITLE:=SCMI transport based on OP-TEE service
DEPENDS:=@(TARGET_rockchip||TARGET_stm32) +kmod-optee
KCONFIG:=CONFIG_ARM_SCMI_TRANSPORT_OPTEE
FILES:=$(LINUX_DIR)/drivers/firmware/arm_scmi/transports/scmi_transport_optee.ko
AUTOLOAD:=$(call AutoProbe,scmi_transport_optee)
endef
define KernelPackage/scmi-transport-optee/description
Arm SCMI transport using an OP-TEE service to communicate with the
platform firmware implementing the SCMI server.
endef
$(eval $(call KernelPackage,scmi-transport-optee))
define KernelPackage/tee-stmm-efi
SUBMENU:=$(OTHER_MENU)
TITLE:=TEE based EFI runtime variable service
DEPENDS:=@TARGET_armsr +kmod-optee
KCONFIG:=CONFIG_TEE_STMM_EFI
FILES:=$(LINUX_DIR)/drivers/firmware/efi/stmm/tee_stmm_efi.ko
AUTOLOAD:=$(call AutoProbe,tee_stmm_efi)
endef
define KernelPackage/tee-stmm-efi/description
Provides EFI runtime variable services through the StandAloneMM Trusted
Application running in OP-TEE.
endef
$(eval $(call KernelPackage,tee-stmm-efi))
define KernelPackage/keys-encrypted
SUBMENU:=$(OTHER_MENU)
TITLE:=encrypted keys on kernel keyring
@@ -991,6 +1078,23 @@ endef
$(eval $(call KernelPackage,tpm))
define KernelPackage/tpm-ftpm-tee
SUBMENU:=$(OTHER_MENU)
TITLE:=TEE based firmware TPM (fTPM)
DEPENDS:=+kmod-tpm +kmod-optee
KCONFIG:=CONFIG_TCG_FTPM_TEE
FILES:=$(LINUX_DIR)/drivers/char/tpm/tpm_ftpm_tee.ko
AUTOLOAD:=$(call AutoLoad,30,tpm_ftpm_tee,1)
endef
define KernelPackage/tpm-ftpm-tee/description
Driver for a firmware TPM (fTPM) running as a Trusted Application inside a
TEE such as OP-TEE. It presents the fTPM to Linux as a TPM 2.0 device,
bound over the TEE client bus or a microsoft,ftpm device tree node.
endef
$(eval $(call KernelPackage,tpm-ftpm-tee))
define KernelPackage/tpm-tis
SUBMENU:=$(OTHER_MENU)
TITLE:=TPM TIS 1.2 Interface / TPM 2.0 FIFO Interface
+18
View File
@@ -143,3 +143,21 @@ define KernelPackage/pse-realtek-mcu-uart/description
endef
$(eval $(call KernelPackage,pse-realtek-mcu-uart))
define KernelPackage/pse-hasivo-hs104
SUBMENU:=$(PSE_MENU)
TITLE:=Hasivo HS104 PSE controller support
KCONFIG:=CONFIG_PSE_HASIVO_HS104
DEPENDS:=+kmod-i2c-core
FILES:=$(LINUX_DIR)/drivers/net/pse-pd/hasivo_hs104.ko
AUTOLOAD:=$(call AutoProbe,hasivo_hs104)
$(call AddDepends/pse-pd)
endef
define KernelPackage/pse-hasivo-hs104/description
Kernel module for the Hasivo HS104 PoE PSE controller chips.
Supports the HS104PTI/HS104PBI single-chip PoE PSE controllers
managing 4 delivery channels for 802.3af/at/bt power over I2C.
endef
$(eval $(call KernelPackage,pse-hasivo-hs104))
+19
View File
@@ -134,6 +134,25 @@ endef
$(eval $(call KernelPackage,rtc-mv))
define KernelPackage/rtc-optee
SUBMENU:=$(RTC_MENU)
TITLE:=OP-TEE based RTC support
DEFAULT:=m if ALL_KMODS && RTC_SUPPORT
DEPENDS:=+kmod-optee
KCONFIG:=CONFIG_RTC_DRV_OPTEE \
CONFIG_RTC_CLASS=y
FILES:=$(LINUX_DIR)/drivers/rtc/rtc-optee.ko
AUTOLOAD:=$(call AutoProbe,rtc-optee)
endef
define KernelPackage/rtc-optee/description
Kernel module for the RTC service exposed by the OP-TEE Trusted
Execution Environment.
endef
$(eval $(call KernelPackage,rtc-optee))
define KernelPackage/rtc-pcf8563
SUBMENU:=$(RTC_MENU)
TITLE:=Philips PCF8563/Epson RTC8564 RTC support
@@ -70,9 +70,9 @@ function setup_phy(phy, config, data) {
config.channel = +config.channel;
config.frequency = get_channel_frequency(config.band, config.channel);
if (config.country) {
log(`Setting country code to ${config.country}`);
system(`iw reg set ${config.country}`);
if (config.country_code) {
log(`Setting country code to ${config.country_code}`);
system(`iw reg set ${config.country_code}`);
}
set_default(config, 'rxantenna', 0xffffffff);
@@ -256,6 +256,7 @@ function setup() {
// fallthrough
case 'sta':
data.ap_start_disabled = true;
data.channel_follow = true;
let config = supplicant.generate(supplicant_data, data, v);
if (mode == "mesh")
config_add_mesh_params(config, v.config);
@@ -4,11 +4,11 @@ import * as libuci from 'uci';
import { md5 } from 'digest';
import * as fs from 'fs';
import { append, append_raw, append_value, append_vars, append_string_vars, comment, push_config, set_default, touch_file } from 'wifi.common';
import { append, append_raw, append_value, append_vars, append_list, append_string_vars, comment, push_config, set_default, touch_file } from 'wifi.common';
import * as netifd from 'wifi.netifd';
import * as iface from 'wifi.iface';
function iface_setup(config) {
function iface_setup(config, phy, num_global_macaddr, macaddr_base) {
switch(config.fixup) {
case 'owe':
config.ignore_broadcast_ssid = true;
@@ -22,7 +22,11 @@ function iface_setup(config) {
config.owe_transition_ssid = config.ssid + 'OWE';
config.encryption = 'none';
config.ignore_broadcast_ssid = false;
iface.prepare(config);
/* the transition BSS needs its own address, not the main BSS's */
config.macaddr = null;
delete config.default_macaddr;
delete config.random_macaddr;
iface.prepare(config, phy, num_global_macaddr, macaddr_base);
break;
}
@@ -51,6 +55,10 @@ function iface_setup(config) {
config.wmm_enabled = 1;
append_string_vars(config, [ 'ssid2' ]);
/* vendor_elements is a single concatenated hex blob, not one per line */
if (type(config.vendor_elements) == 'array')
config.vendor_elements = join('', config.vendor_elements);
append_vars(config, [
'ctrl_interface', 'ap_isolate', 'max_num_sta', 'ap_max_inactivity', 'airtime_bss_weight',
'airtime_bss_limit', 'airtime_sta_weight', 'bss_load_update_period', 'chan_util_avg_period',
@@ -69,7 +77,7 @@ function iface_authentication_server(config) {
append_vars(config, [ 'auth_server_port', 'auth_server_shared_secret' ]);
}
append_vars(config, [ 'radius_auth_req_attr' ]);
append_list(config, [ 'radius_auth_req_attr' ]);
}
function iface_accounting_server(config) {
@@ -78,7 +86,7 @@ function iface_accounting_server(config) {
append_vars(config, [ 'acct_server_port', 'acct_server_shared_secret' ]);
}
append_vars(config, [ 'radius_acct_req_attr' ]);
append_list(config, [ 'radius_acct_req_attr' ]);
}
function iface_auth_type(config, band) {
@@ -522,8 +530,9 @@ function iface_hs20(config) {
append_vars(config, [
'hs20', 'disable_dgaf', 'anqp_domain_id', 'hs20_deauth_req_timeout',
'hs20_wan_metrics', 'hs20_operating_class', 'hs20_t_c_filename', 'hs20_t_c_timestamp',
'hs20_t_c_server_url', 'hs20_conn_capab'
'hs20_t_c_server_url'
]);
append_list(config, [ 'hs20_conn_capab' ]);
}
function iface_interworking(config) {
@@ -536,20 +545,21 @@ function iface_interworking(config) {
config.domain_name = join(',', config.domain_name);
if (config.anqp_3gpp_cell_net)
config.domain_name = join(',', config.anqp_3gpp_cell_net);
config.anqp_3gpp_cell_net = join(';', config.anqp_3gpp_cell_net);
append_vars(config, [
'interworking', 'internet', 'asra', 'uesa', 'access_network_type', 'hessid', 'venue_group',
'venue_type', 'network_auth_type', 'gas_address3', 'roaming_consortium', 'anqp_elem', 'nai_realm',
'venue_name', 'venue_url', 'domain_name', 'anqp_3gpp_cell_net',
'venue_type', 'network_auth_type', 'gas_address3', 'roaming_consortium',
'domain_name', 'anqp_3gpp_cell_net',
]);
append_list(config, [ 'anqp_elem', 'nai_realm', 'venue_name', 'venue_url' ]);
}
export function generate(interface, data, config, vlans, stas, phy_features) {
config.ctrl_interface = '/var/run/hostapd';
config.start_disabled = data.ap_start_disabled;
iface_setup(config);
iface_setup(config, data.phy + data.phy_suffix, data.config.num_global_macaddr, data.config.macaddr_base);
iface.parse_encryption(config, data.config, phy_features);
if (data.config.band == '6g') {
@@ -72,6 +72,20 @@ export function append_vars(dict, keys) {
append(key, dict[key]);
};
/* emit one "key=value" line per list element (for options hostapd parses
* one entry per line, which must not be space-joined) */
export function append_list(dict, keys) {
for (let key in keys) {
let val = dict[key];
if (val == null)
continue;
if (type(val) != 'array')
val = [ val ];
for (let v in val)
append(key, v);
}
};
export function append_string_vars(dict, keys) {
for (let key in keys)
append_string(key, dict[key]);
@@ -474,7 +474,7 @@ function device_htmode_append(config) {
}
function device_extended_features(data, flag) {
return !!(data[flag / 8] | (1 << (flag % 8)));
return !!(data[flag / 8] & (1 << (flag % 8)));
}
function device_capabilities(config) {
@@ -589,6 +589,10 @@ export function setup(data) {
append('\n#num_global_macaddr', data.config.num_global_macaddr);
if (data.config.macaddr_base)
append('\n#macaddr_base', data.config.macaddr_base);
if (data.config.frequency)
append('\n#frequency', data.config.frequency);
if (data.channel_follow)
append('\n#channel_follow', 1);
let has_ap;
for (let k, interface in data.interfaces) {
@@ -58,7 +58,7 @@ export function ratelist(rates) {
};
function setup_sta(data, config) {
iface.parse_encryption(config);
iface.parse_encryption(config, data);
if (config.auth_type in [ 'sae', 'owe', 'eap2', 'eap192', 'dpp' ])
config.ieee80211w = 2;
@@ -141,6 +141,8 @@ function setup_sta(data, config) {
if (config.mode == 'mesh' || config.auth_type == 'sae')
config.sae_password = `"${config.key}"`;
else if (length(config.key) == 64)
config.psk = config.key;
else
config.psk = `"${config.key}"`;
@@ -190,6 +192,8 @@ function setup_sta(data, config) {
if (config.wpa_pairwise == 'GCMP') {
config.pairwise = 'GCMP';
config.group = 'GCMP';
} else if (config.wpa_pairwise) {
config.pairwise = config.wpa_pairwise;
}
config.key_mgmt ??= 'NONE';
@@ -230,6 +234,7 @@ function setup_sta(data, config) {
'domain_match', 'domain_match2',
'domain_suffix_match', 'domain_suffix_match2',
'private_key', 'private_key_passwd', 'private_key2', 'private_key2_passwd',
'dpp_connector',
]);
network_append_vars(config, [
'rsn_overriding', 'scan_ssid', 'noscan', 'disabled', 'multi_ap_profile', 'multi_ap_backhaul_sta',
@@ -238,7 +243,7 @@ function setup_sta(data, config) {
'disable_ht', 'disable_ht40', 'disable_vht', 'vht', 'max_oper_chwidth',
'ht40', 'beacon_int', 'ieee80211w', 'rates', 'mesh_basic_rates', 'mcast_rate',
'bssid_blacklist', 'bssid_whitelist', 'erp', 'eap', 'phase2',
'dpp_connector', 'dpp_csign', 'dpp_netaccesskey',
'dpp_csign', 'dpp_netaccesskey',
]);
}
@@ -61,7 +61,7 @@ function handle_link(dev, data, up)
};
if (ap && config.multicast_to_unicast != null)
dev_data.multicast_to_unicast = config.multicast_to_unicast;
dev_data.multicast_to_unicast = config.multicast_to_unicast ? 1 : 0;
if (data.type == "vif" && config.mode == "ap") {
dev_data.wireless_proxyarp = !!config.proxy_arp;
@@ -173,6 +173,9 @@ function wdev_teardown_cb(wdev)
return;
}
if (wdev.config_change)
wdev_config_init(wdev);
wdev.setup();
}
@@ -443,6 +446,14 @@ function check()
if (!wdev_update_disabled_vifs(this))
return;
/*
* Defer applying a new config while a handler run is in progress: the
* running script keeps its start-time config, and the setup/teardown
* callback re-applies once it finishes (config_change stays set).
*/
if (this.state != "up" && this.state != "down")
return;
wdev_config_init(this);
this.setup();
}
@@ -453,6 +464,14 @@ function wdev_mark_up(wdev)
if (wdev.state != "setup")
return;
if (wdev.cancel_setup || !wdev.autostart || wdev.delete) {
delete wdev.cancel_setup;
wdev.teardown();
return 0;
}
wdev_reset(wdev);
for (let section, data in wdev.handler_data) {
if (data.ifname)
handle_link(data.ifname, data, true);
@@ -536,7 +555,7 @@ function notify(req)
function hotplug(name, add)
{
let dev = name;
let m = match(name, /(.+)\.sta.+/);
let m = match(name, /^(.+)\.sta[0-9]+$/);
if (m)
name = m[1];
@@ -594,7 +613,7 @@ function status()
});
}
return {
up: this.state == "up",
up: this.state == "up" && !this.data.config.disabled,
pending: this.state == "setup" || this.state == "teardown",
autostart: this.autostart,
disabled: !!this.data.config.disabled,
@@ -275,7 +275,7 @@ function config_init(uci)
continue;
dev = devices[radio] = {
name,
name: radio,
config,
vif: [],
@@ -337,7 +337,7 @@ function config_init(uci)
sta: []
};
if (vif.vlans)
vif_data.vlans = vif.vlans;
vif_data.vlan = vif.vlans;
if (vif.stations)
vif_data.sta = vif.stations;
push(dev.vif, vif_data);
@@ -298,8 +298,7 @@ const phy_proto = {
},
wdev_add: function(name, data) {
let phydev = this;
wdev_create(this.phy, name, {
return wdev_create(this.phy, name, {
...data,
radio: this.radio,
});
@@ -90,17 +90,6 @@ function delete_ifname(config)
delete config[key].ifname;
}
function add_existing(phydev, config)
{
phydev.for_each_wdev((wdev) => {
if (config[wdev])
return;
if (trim(readfile(`/sys/class/net/${wdev}/operstate`)) == "down")
config[wdev] = {};
});
}
function usage()
{
warn(`Usage: ${basename(sourcepath())} <phy> <command> [<arguments>]
@@ -137,12 +126,11 @@ const commands = {
if (type(old_config) == "object")
config.data = old_config;
add_existing(phydev, config.data);
add_ifname(config.data);
drop_inactive(config.data);
let ubus = libubus.connect();
let data = ubus.call("hostapd", "config_get_macaddr_list", { phy: phydev.name, radio: phydev.radio ?? -1 });
let data = ubus.call("hostapd", "config_get_macaddr_list", { phy: phydev.phy, radio: int(phydev.radio ?? -1) });
let macaddr_list = [];
if (type(data) == "object" && data.macaddr)
macaddr_list = data.macaddr;
@@ -1,4 +1,5 @@
let libubus = require("ubus");
import * as uloop from "uloop";
import { open, readfile, access } from "fs";
import { wdev_remove, is_equal, vlist_new, phy_is_fullmac, phy_open, wdev_set_radio_mask, wdev_set_up } from "common";
@@ -130,6 +131,15 @@ function iface_freq_info(iface, config, params)
if (freq < 4000)
width = 0;
/*
* 6 GHz has no HT Operation IE, so the secondary channel offset cannot
* be derived the usual way. For wide channels pass a null offset so the
* C helper auto-derives it and computes the segment centre frequency; a
* 0 offset would make it skip the centre calculation.
*/
if (freq > 5925 && width > 0 && sec_offset == 0)
sec_offset = null;
return hostapd.freq_info(freq, sec_offset, width);
}
@@ -311,11 +321,22 @@ function iface_macaddr_init(phydev, config, macaddr_list)
return phydev.macaddr_init(macaddr_list, macaddr_data);
}
function csa_timer_cancel(name)
{
let timers = hostapd.data.csa_timer;
if (timers && timers[name]) {
timers[name].cancel();
delete timers[name];
}
}
function iface_restart(phydev, config, old_config)
{
let phy = phydev.name;
let pending = hostapd.data.pending_config[phy];
csa_timer_cancel(phy);
if (pending)
pending.abort();
@@ -516,13 +537,155 @@ function get_config_bss(name, config, idx)
return if_bss[ifname];
}
const radio_chan_fields = [
"op_class",
"vht_oper_chwidth", "vht_oper_centr_freq_seg0_idx",
"he_oper_chwidth", "he_oper_centr_freq_seg0_idx",
"eht_oper_chwidth", "eht_oper_centr_freq_seg0_idx",
];
function radio_line_is_chan(line)
{
return index(radio_chan_fields, split(line, "=", 2)[0]) >= 0;
}
// The HT40+/- direction in ht_capab is channel-derived and re-applied through
// the CSA secondary-channel offset, so strip it when comparing the base config;
// the remaining ht_capab bits are device capabilities that require a restart.
function radio_base(radio)
{
return map(filter(radio.data, (line) => !radio_line_is_chan(line)), (line) => {
if (substr(line, 0, 9) != "ht_capab=")
return line;
return replace(replace(line, "[HT40+]", ""), "[HT40-]", "");
});
}
function radio_reload_class(old_radio, new_radio)
{
if (is_equal(old_radio, new_radio))
return "same";
// anything beyond channel/width, or the channel-follow flag flipping,
// needs a full restart
if (!is_equal(radio_base(old_radio), radio_base(new_radio)) ||
(!old_radio.channel_follow) != (!new_radio.channel_follow))
return "restart";
// base identical and apsta flag unchanged: only treat this as a channel
// switch if the channel or its derived width lines actually differ (the
// derived frequency field alone appearing is not a real change)
if (old_radio.channel == new_radio.channel &&
is_equal(filter(old_radio.data, radio_line_is_chan),
filter(new_radio.data, radio_line_is_chan)))
return "same";
return "channel";
}
function iface_channel_is_dfs(radio)
{
let freq = radio.frequency;
/* 5 GHz DFS sub-bands; 2.4 GHz and 6 GHz have no radar channels */
return freq && ((freq >= 5250 && freq <= 5330) ||
(freq >= 5490 && freq <= 5730));
}
function iface_csa_check(name)
{
if (hostapd.data.csa_timer)
delete hostapd.data.csa_timer[name];
let config = hostapd.data.config[name];
let iface = hostapd.interfaces[name];
if (!config || !iface || !iface.csa_in_progress())
return;
hostapd.printf(`Config channel switch on phy ${name} did not complete, restarting`);
let phydev = phy_open(config.phy, config.radio_idx);
if (phydev)
iface_restart(phydev, config, config);
}
function iface_channel_switch(name, config)
{
let radio = config.radio;
/*
* The runtime channel is followed from a co-located supplicant
* interface (STA/mesh/adhoc) via apsta_state; the AP never picks its
* own channel here. Adopt the new fallback channel without touching the
* running BSSes.
*/
if (radio.channel_follow)
return true;
/*
* A single iface-level CSA cannot coordinate the links of an MLD AP
* that span multiple radios, and DFS targets need CAC before use.
* Fall back to a full restart for both.
*/
for (let bss in config.bss)
if (bss.mld_ap)
return false;
if (!radio.frequency || iface_channel_is_dfs(radio))
return false;
let iface = hostapd.interfaces[name];
if (!iface || iface.state() != "ENABLED")
return false;
let freq_info = iface_freq_info(iface, config, { frequency: radio.frequency });
if (!freq_info)
return false;
freq_info.csa_count = 10;
if (!iface.switch_channel(freq_info))
return false;
hostapd.printf(`Channel switch to ${radio.channel} on phy ${name}`);
/*
* Verify the switch actually completes; if the driver never finishes it,
* fall back to a full restart. Armed only here, so ubus- or apsta-
* triggered channel switches are left to their own recovery.
*/
hostapd.data.csa_timer ??= {};
csa_timer_cancel(name);
let beacon_int = 100;
for (let line in config.radio.data) {
let m = match(line, /^beacon_int=([0-9]+)/);
if (m) {
beacon_int = int(m[1]);
break;
}
}
hostapd.data.csa_timer[name] = uloop.timer(freq_info.csa_count * beacon_int + 2000,
() => iface_csa_check(name));
return true;
}
function iface_reload_config(name, phydev, config, old_config)
{
let phy = phydev.name;
if (!old_config || !is_equal(old_config.radio, config.radio))
if (!old_config)
return false;
switch (radio_reload_class(old_config.radio, config.radio)) {
case "restart":
return false;
case "channel":
if (!iface_channel_switch(name, config))
return false;
break;
}
if (is_equal(old_config.bss, config.bss))
return true;
@@ -696,7 +859,7 @@ function iface_reload_config(name, phydev, config, old_config)
// with the bssid of a reused interface. reassign the reused interface
if (!bsscfg.default_macaddr) {
// can't update bssid of the first BSS, need to restart
if (!mac_idx < 0)
if (mac_idx <= 0)
return false;
bsscfg = config.bss[mac_idx];
@@ -846,6 +1009,11 @@ function iface_set_config(name, config)
{
let old_config = hostapd.data.config[name];
if (!config) {
delete hostapd.data.config[name];
return iface_config_remove(name, old_config);
}
hostapd.data.config[name] = config;
let phy = config.phy;
@@ -916,6 +1084,9 @@ function iface_load_config(phy, radio, filename)
if (!val[0])
continue;
if (substr(line, 0, 2) == "# ")
continue;
if (val[0] == "interface") {
bss = config_add_bss(config, val[1]);
break;
@@ -926,6 +1097,16 @@ function iface_load_config(phy, radio, filename)
continue;
}
if (val[0] == "#frequency") {
config.radio.frequency = int(val[1]);
continue;
}
if (val[0] == "#channel_follow") {
config.radio.channel_follow = int(val[1]) == 1;
continue;
}
if (val[0] == "#num_global_macaddr")
config[substr(val[0], 1)] = int(val[1]);
else if (val[0] == "#macaddr_base")
@@ -946,6 +1127,9 @@ function iface_load_config(phy, radio, filename)
if (!val[0])
continue;
if (substr(line, 0, 2) == "# ")
continue;
if (val[0] == "bssid") {
bss.bssid = lc(val[1]);
continue;
@@ -1070,7 +1254,7 @@ function mld_set_config(config)
let prev_mld = { ...hostapd.data.mld };
let new_mld = {};
let phy_list = {};
let new_config = !length(prev_mld) && length(new_mld);
let new_config = !length(prev_mld) && length(config);
hostapd.printf(`Set MLD config: ${keys(config)}`);
@@ -552,6 +552,8 @@ let main_obj = {
for (let ifname in phy.data)
iface_stop(phy.data[ifname]);
for (let name, data in wpas.data.mld) {
if (data.phy != phy.name)
continue;
data.radio_mask_present &= ~radio_mask;
if (data.radio_mask_up & radio_mask)
mld_update_iface(name, data);
@@ -561,6 +563,8 @@ let main_obj = {
let found;
for (let name, data in wpas.data.mld) {
if (data.phy != phy.name)
continue;
if (!(data.radio_mask & radio_mask))
continue;
data.radio_mask_present |= radio_mask;
@@ -604,9 +608,12 @@ let main_obj = {
let ifnames = keys(phy.data);
let radio_mask = phy.radio != null ? (1 << phy.radio) : 0;
for (let name, data in wpas.data.mld)
for (let name, data in wpas.data.mld) {
if (data.phy != phy.name)
continue;
if (data.radio_mask_up & radio_mask)
push(ifnames, name);
}
for (let ifname in ifnames) {
try {
@@ -297,6 +297,14 @@ uc_hostapd_bss_set_config(uc_vm_t *vm, size_t nargs)
started = hapd->started;
__uc_hostapd_bss_stop(hapd);
/*
* start_disabled is only meaningful for the initial apsta bring-up. When
* re-applying config to a BSS that was already started, clear it so the
* restart keeps beaconing instead of silently going quiet.
*/
if (started)
conf->bss[idx]->start_disabled = 0;
old_bss = hapd->conf;
for (i = 0; i < iface->conf->num_bss; i++)
if (iface->conf->bss[i] == hapd->conf)
@@ -372,6 +380,9 @@ uc_hostapd_bss_delete(uc_vm_t *vm, size_t nargs)
hostapd_drv_stop_ap(hapd);
hostapd_bss_deinit(hapd);
/* deinit skips these for a bss that never started; both are idempotent */
hostapd_ucode_free_bss(hapd);
hostapd_ubus_free_bss(hapd);
hostapd_remove_iface_bss_conf(iface->conf, hapd->conf);
hostapd_config_free_bss(hapd->conf);
#ifdef CONFIG_IEEE80211BE
@@ -420,7 +431,7 @@ uc_hostapd_iface_add_bss(uc_vm_t *vm, size_t nargs)
{
struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
struct hostapd_bss_config *bss;
struct hostapd_config *conf;
struct hostapd_config *conf = NULL;
struct hostapd_data *hapd;
uc_value_t *file = uc_fn_arg(0);
uc_value_t *index = uc_fn_arg(1);
@@ -739,10 +750,22 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
intval = ucv_int64_get(ucv_object_get(info, "oper_chwidth", NULL));
if (errno)
intval = hostapd_get_oper_chwidth(conf);
if (intval)
csa.freq_params.bandwidth = 40 << intval;
else
switch (intval) {
case CONF_OPER_CHWIDTH_80MHZ:
case CONF_OPER_CHWIDTH_80P80MHZ:
/* 80+80 uses an 80 MHz primary segment plus center_freq2 */
csa.freq_params.bandwidth = 80;
break;
case CONF_OPER_CHWIDTH_160MHZ:
csa.freq_params.bandwidth = 160;
break;
case CONF_OPER_CHWIDTH_320MHZ:
csa.freq_params.bandwidth = 320;
break;
default:
csa.freq_params.bandwidth = csa.freq_params.sec_channel_offset ? 40 : 20;
break;
}
if ((intval = ucv_int64_get(ucv_object_get(info, "frequency", NULL))) && !errno)
csa.freq_params.freq = intval;
@@ -757,6 +780,17 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
return ucv_boolean_new(!ret);
}
static uc_value_t *
uc_hostapd_iface_csa_in_progress(uc_vm_t *vm, size_t nargs)
{
struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
if (!iface)
return NULL;
return ucv_boolean_new(hostapd_csa_in_progress(iface));
}
static uc_value_t *
uc_hostapd_bss_rename(uc_vm_t *vm, size_t nargs)
{
@@ -840,13 +874,15 @@ int hostapd_ucode_sta_auth(struct hostapd_data *hapd, struct sta_info *sta)
size_t str_len;
cur_psk = ucv_array_get(cur, i);
if (ucv_type(cur_psk) != UC_STRING)
continue;
str = ucv_string_get(cur_psk);
str_len = strlen(str);
if (!str || str_len < 8 || str_len > 64)
if (str_len < 8 || str_len > 64)
continue;
p = os_zalloc(sizeof(*p));
if (len == 64) {
if (str_len == 64) {
if (hexstr2bin(str, p->psk, PMK_LEN) < 0) {
free(p);
continue;
@@ -1213,6 +1249,7 @@ int hostapd_ucode_init(struct hapd_interfaces *ifaces)
{ "stop", uc_hostapd_iface_stop },
{ "start", uc_hostapd_iface_start },
{ "switch_channel", uc_hostapd_iface_switch_channel },
{ "csa_in_progress", uc_hostapd_iface_csa_in_progress },
};
uc_value_t *data, *proto;
@@ -1280,9 +1317,10 @@ void hostapd_ucode_free_bss(struct hostapd_data *hapd)
if (wpa_ucode_call_prepare("bss_remove"))
return;
uc_value_push(ucv_string_new(hapd->iface->phy));
uc_value_push(ucv_string_new(hapd->conf->iface));
uc_value_push(ucv_get(val));
ucv_put(wpa_ucode_call(2));
ucv_put(wpa_ucode_call(3));
ucv_put(val);
}
@@ -139,6 +139,9 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
case 2:
chanwidth = CONF_OPER_CHWIDTH_160MHZ;
break;
case 3:
chanwidth = CONF_OPER_CHWIDTH_320MHZ;
break;
default:
return NULL;
}
+2 -2
View File
@@ -8,13 +8,13 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=ethtool
PKG_VERSION:=7.0
PKG_VERSION:=7.1
PKG_RELEASE:=1
PKG_MAINTAINER:=Felix Fietkau <nbd@nbd.name>
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.xz
PKG_SOURCE_URL:=@KERNEL/software/network/ethtool
PKG_HASH:=660bf9725a7871343a0d232068a7634fbcfb69b6c2f8eff455827faefb0cd162
PKG_HASH:=4d78c26edc0255bc92f4b995b5fd66108d75ff966ed4694f6025a6d370bc2496
PKG_LICENSE:=GPL-2.0
PKG_LICENSE_FILES:=COPYING
+1 -1
View File
@@ -215,7 +215,7 @@ fis_remap(struct fis_part *old, int n_old, struct fis_part *new, int n_new)
memmove(desc, last, end - tmp);
if (desc < last) {
tmp = end - (last - desc) * sizeof(struct fis_image_desc);
memset(tmp, 0xff, tmp - end);
memset(tmp, 0xff, end - tmp);
}
}
+3 -3
View File
@@ -12,9 +12,9 @@ PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL=$(PROJECT_GIT)/project/rpcd.git
PKG_MIRROR_HASH:=b15b42e9f27a35030f488d7a61a3b2f505f79b5675455b20f1336acb1d99686a
PKG_SOURCE_DATE:=2026-06-04
PKG_SOURCE_VERSION:=28faf6403792d25b9826043aaf37880624c19568
PKG_MIRROR_HASH:=ca2498a25c69e6b6e8fe1bcc5865a244e409674b1ac2e2e36c0e4bb04b11aba8
PKG_SOURCE_DATE:=2026-07-19
PKG_SOURCE_VERSION:=e37ed9d814699098eb7e26c8b33c054840782dfb
PKG_MAINTAINER:=Jo-Philipp Wich <jo@mein.io>
PKG_LICENSE:=ISC
+1 -1
View File
@@ -1,7 +1,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=ucode-mod-bpf
PKG_RELEASE:=1
PKG_RELEASE:=2
PKG_LICENSE:=ISC
PKG_MAINTAINER:=Felix Fietkau <nbd@nbd.name>
+548 -41
View File
@@ -32,7 +32,9 @@ struct uc_bpf_fd {
struct uc_bpf_map {
struct uc_bpf_fd fd; /* must be first */
unsigned int type;
unsigned int key_size, val_size;
unsigned int max_entries;
};
struct uc_bpf_map_iter {
@@ -129,7 +131,7 @@ uc_bpf_module_set_opts(struct bpf_object *obj, uc_value_t *opts)
}
if (!map)
err_return_int(errno, "rodata map");
err_return_int(ENOENT, "rodata map");
if (bpf_map__set_initial_value(map, ucv_string_get(val),
ucv_string_length(val)))
@@ -187,8 +189,9 @@ uc_bpf_open_module(uc_vm_t *vm, size_t nargs)
}
static uc_value_t *
uc_bpf_map_create(uc_vm_t *vm, uc_value_t *mod, int fd, unsigned int key_size,
unsigned int val_size, bool close)
uc_bpf_map_create(uc_vm_t *vm, uc_value_t *mod, int fd, unsigned int type,
unsigned int key_size, unsigned int val_size,
unsigned int max_entries, bool close)
{
struct uc_bpf_map *uc_map;
uc_value_t *res;
@@ -196,8 +199,10 @@ uc_bpf_map_create(uc_vm_t *vm, uc_value_t *mod, int fd, unsigned int key_size,
res = ucv_resource_create_ex(vm, "bpf.map", (void **)&uc_map, 1, sizeof(*uc_map));
ucv_resource_value_set(res, 0, ucv_get(mod));
uc_map->fd.fd = fd;
uc_map->type = type;
uc_map->key_size = key_size;
uc_map->val_size = val_size;
uc_map->max_entries = max_entries;
uc_map->fd.close = close;
return res;
@@ -218,12 +223,26 @@ uc_bpf_prog_create(uc_vm_t *vm, uc_value_t *mod, int fd, bool close)
}
static uc_value_t *
uc_bpf_open_map(uc_vm_t *vm, size_t nargs)
uc_bpf_open_map_fd(uc_vm_t *vm, int fd)
{
struct bpf_map_info info;
uc_value_t *path = uc_fn_arg(0);
__u32 len = sizeof(info);
int err;
err = bpf_obj_get_info_by_fd(fd, &info, &len);
if (err) {
close(fd);
err_return(errno, NULL);
}
return uc_bpf_map_create(vm, NULL, fd, info.type, info.key_size,
info.value_size, info.max_entries, true);
}
static uc_value_t *
uc_bpf_open_map(uc_vm_t *vm, size_t nargs)
{
uc_value_t *path = uc_fn_arg(0);
int fd;
if (ucv_type(path) != UC_STRING)
@@ -233,13 +252,23 @@ uc_bpf_open_map(uc_vm_t *vm, size_t nargs)
if (fd < 0)
err_return(errno, NULL);
err = bpf_obj_get_info_by_fd(fd, &info, &len);
if (err) {
close(fd);
err_return(errno, NULL);
}
return uc_bpf_open_map_fd(vm, fd);
}
return uc_bpf_map_create(vm, NULL, fd, info.key_size, info.value_size, true);
static uc_value_t *
uc_bpf_open_map_id(uc_vm_t *vm, size_t nargs)
{
uc_value_t *id = uc_fn_arg(0);
int fd;
if (ucv_type(id) != UC_INTEGER)
err_return(EINVAL, "map id");
fd = bpf_map_get_fd_by_id(ucv_int64_get(id));
if (fd < 0)
err_return(errno, NULL);
return uc_bpf_open_map_fd(vm, fd);
}
static uc_value_t *
@@ -258,6 +287,22 @@ uc_bpf_open_program(uc_vm_t *vm, size_t nargs)
return uc_bpf_prog_create(vm, NULL, fd, true);
}
static uc_value_t *
uc_bpf_open_program_id(uc_vm_t *vm, size_t nargs)
{
uc_value_t *id = uc_fn_arg(0);
int fd;
if (ucv_type(id) != UC_INTEGER)
err_return(EINVAL, "program id");
fd = bpf_prog_get_fd_by_id(ucv_int64_get(id));
if (fd < 0)
err_return(errno, NULL);
return uc_bpf_prog_create(vm, NULL, fd, true);
}
static uc_value_t *
uc_bpf_module_get_maps(uc_vm_t *vm, size_t nargs)
{
@@ -295,7 +340,9 @@ uc_bpf_module_get_map(uc_vm_t *vm, size_t nargs)
if (fd < 0)
err_return(EINVAL, NULL);
return uc_bpf_map_create(vm, _uc_fn_this_res(vm), fd, bpf_map__key_size(map), bpf_map__value_size(map), false);
return uc_bpf_map_create(vm, _uc_fn_this_res(vm), fd, bpf_map__type(map),
bpf_map__key_size(map), bpf_map__value_size(map),
bpf_map__max_entries(map), false);
}
static uc_value_t *
@@ -339,28 +386,40 @@ uc_bpf_module_get_program(uc_vm_t *vm, size_t nargs)
}
static void *
uc_bpf_map_arg(uc_value_t *val, const char *kind, unsigned int size)
uc_bpf_map_arg(uc_value_t *val, const char *kind, unsigned int size,
uint64_t *val_int)
{
static union {
uint32_t u32;
uint64_t u64;
} val_int;
switch (ucv_type(val)) {
case UC_INTEGER:
if (size == 4)
val_int.u32 = ucv_int64_get(val);
*(uint32_t *)val_int = ucv_int64_get(val);
else if (size == 8)
val_int.u64 = ucv_int64_get(val);
*val_int = ucv_int64_get(val);
else
break;
return &val_int;
return val_int;
case UC_STRING:
if (size != ucv_string_length(val))
break;
return ucv_string_get(val);
case UC_RESOURCE: {
struct uc_bpf_fd *f;
f = ucv_resource_data(val, "bpf.map");
if (!f)
f = ucv_resource_data(val, "bpf.program");
if (!f)
err_return(EINVAL, "%s type", kind);
if (size != 4)
break;
*(uint32_t *)val_int = f->fd;
return val_int;
}
default:
err_return(EINVAL, "%s type", kind);
}
@@ -368,25 +427,174 @@ uc_bpf_map_arg(uc_value_t *val, const char *kind, unsigned int size)
err_return(EINVAL, "%s size mismatch (expected: %d)", kind, size);
}
static bool
uc_bpf_map_key_arg(struct uc_bpf_map *map, uc_value_t *a_key, uint64_t *buf,
void **key)
{
if (!map->key_size) {
if (a_key) {
set_error(EINVAL, "map has no key");
return false;
}
*key = NULL;
return true;
}
*key = uc_bpf_map_arg(a_key, "key", map->key_size, buf);
return *key != NULL;
}
static bool
uc_bpf_map_is_percpu(struct uc_bpf_map *map)
{
switch (map->type) {
case BPF_MAP_TYPE_PERCPU_HASH:
case BPF_MAP_TYPE_PERCPU_ARRAY:
case BPF_MAP_TYPE_LRU_PERCPU_HASH:
case BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE:
return true;
default:
return false;
}
}
static unsigned int
uc_bpf_map_val_stride(struct uc_bpf_map *map)
{
return (map->val_size + 7) & ~7U;
}
static bool
uc_bpf_map_val_len(struct uc_bpf_map *map, unsigned int *len, int *num_cpus)
{
*len = map->val_size;
*num_cpus = 1;
if (!uc_bpf_map_is_percpu(map))
return true;
*num_cpus = libbpf_num_possible_cpus();
if (*num_cpus <= 0) {
set_error(errno, "number of CPUs");
return false;
}
*len = uc_bpf_map_val_stride(map) * *num_cpus;
return true;
}
static uc_value_t *
uc_bpf_map_val_read(uc_vm_t *vm, struct uc_bpf_map *map, void *val,
int num_cpus)
{
unsigned int stride = uc_bpf_map_val_stride(map);
uc_value_t *rv;
int i;
if (!uc_bpf_map_is_percpu(map))
return ucv_string_new_length(val, map->val_size);
rv = ucv_array_new(vm);
for (i = 0; i < num_cpus; i++)
ucv_array_set(rv, i, ucv_string_new_length((char *)val + i * stride,
map->val_size));
return rv;
}
static bool
uc_bpf_map_val_copy(struct uc_bpf_map *map, uc_value_t *a_val, void *dest)
{
uint64_t val_int;
void *val;
val = uc_bpf_map_arg(a_val, "value", map->val_size, &val_int);
if (!val)
return false;
memcpy(dest, val, map->val_size);
return true;
}
static void *
uc_bpf_map_val_arg(struct uc_bpf_map *map, uc_value_t *a_val, void *buf,
int num_cpus)
{
unsigned int stride = uc_bpf_map_val_stride(map);
int i;
if (!uc_bpf_map_is_percpu(map))
return uc_bpf_map_arg(a_val, "value", map->val_size, buf);
memset(buf, 0, stride * num_cpus);
if (ucv_type(a_val) != UC_ARRAY) {
if (!uc_bpf_map_val_copy(map, a_val, buf))
return NULL;
for (i = 1; i < num_cpus; i++)
memcpy((char *)buf + i * stride, buf, map->val_size);
return buf;
}
if (ucv_array_length(a_val) != (size_t)num_cpus)
err_return(EINVAL, "value count mismatch (expected: %d)", num_cpus);
for (i = 0; i < num_cpus; i++)
if (!uc_bpf_map_val_copy(map, ucv_array_get(a_val, i),
(char *)buf + i * stride))
return NULL;
return buf;
}
static uc_value_t *
uc_bpf_map_info(uc_vm_t *vm, size_t nargs)
{
struct uc_bpf_map *map = uc_fn_thisval("bpf.map");
uc_value_t *rv;
if (!map)
err_return(EINVAL, NULL);
rv = ucv_object_new(vm);
ucv_object_add(rv, "type", ucv_int64_new(map->type));
ucv_object_add(rv, "key_size", ucv_int64_new(map->key_size));
ucv_object_add(rv, "value_size", ucv_int64_new(map->val_size));
ucv_object_add(rv, "max_entries", ucv_int64_new(map->max_entries));
return rv;
}
static uc_value_t *
uc_bpf_map_get(uc_vm_t *vm, size_t nargs)
{
struct uc_bpf_map *map = uc_fn_thisval("bpf.map");
uc_value_t *a_key = uc_fn_arg(0);
unsigned int val_len;
uint64_t key_int;
void *key, *val;
int num_cpus;
if (!map)
err_return(EINVAL, NULL);
key = uc_bpf_map_arg(a_key, "key", map->key_size);
if (!key)
if (!uc_bpf_map_key_arg(map, a_key, &key_int, &key))
return NULL;
val = alloca(map->val_size);
if (!uc_bpf_map_val_len(map, &val_len, &num_cpus))
return NULL;
val = alloca(val_len);
if (bpf_map_lookup_elem(map->fd.fd, key, val))
return NULL;
return ucv_string_new_length(val, map->val_size);
return uc_bpf_map_val_read(vm, map, val, num_cpus);
}
static uc_value_t *
@@ -396,17 +604,23 @@ uc_bpf_map_set(uc_vm_t *vm, size_t nargs)
uc_value_t *a_key = uc_fn_arg(0);
uc_value_t *a_val = uc_fn_arg(1);
uc_value_t *a_flags = uc_fn_arg(2);
unsigned int val_len;
uint64_t key_int;
uint64_t flags;
void *key, *val;
int num_cpus;
if (!map)
err_return(EINVAL, NULL);
key = uc_bpf_map_arg(a_key, "key", map->key_size);
if (!key)
if (!uc_bpf_map_key_arg(map, a_key, &key_int, &key))
return NULL;
val = uc_bpf_map_arg(a_val, "value", map->val_size);
if (!uc_bpf_map_val_len(map, &val_len, &num_cpus))
return NULL;
val = alloca(val_len);
val = uc_bpf_map_val_arg(map, a_val, val, num_cpus);
if (!val)
return NULL;
@@ -420,7 +634,7 @@ uc_bpf_map_set(uc_vm_t *vm, size_t nargs)
if (bpf_map_update_elem(map->fd.fd, key, val, flags))
return NULL;
return ucv_string_new_length(val, map->val_size);
return uc_bpf_map_val_read(vm, map, val, num_cpus);
}
static uc_value_t *
@@ -429,14 +643,16 @@ uc_bpf_map_delete(uc_vm_t *vm, size_t nargs)
struct uc_bpf_map *map = uc_fn_thisval("bpf.map");
uc_value_t *a_key = uc_fn_arg(0);
uc_value_t *a_return = uc_fn_arg(1);
unsigned int val_len;
void *key, *val = NULL;
uint64_t key_int;
int num_cpus;
int ret;
if (!map)
err_return(EINVAL, NULL);
key = uc_bpf_map_arg(a_key, "key", map->key_size);
if (!key)
if (!uc_bpf_map_key_arg(map, a_key, &key_int, &key))
return NULL;
if (!ucv_is_truish(a_return)) {
@@ -445,11 +661,14 @@ uc_bpf_map_delete(uc_vm_t *vm, size_t nargs)
return ucv_boolean_new(ret == 0);
}
val = alloca(map->val_size);
if (!uc_bpf_map_val_len(map, &val_len, &num_cpus))
return NULL;
val = alloca(val_len);
if (bpf_map_lookup_and_delete_elem(map->fd.fd, key, val))
return NULL;
return ucv_string_new_length(val, map->val_size);
return uc_bpf_map_val_read(vm, map, val, num_cpus);
}
static uc_value_t *
@@ -520,6 +739,9 @@ uc_bpf_map_iter_next(uc_vm_t *vm, size_t nargs)
struct uc_bpf_map_iter *iter = uc_fn_thisval("bpf.map_iter");
uc_value_t *rv;
if (!iter)
err_return(EINVAL, NULL);
if (!iter->has_next)
return NULL;
@@ -536,15 +758,22 @@ uc_bpf_map_iter_next_int(uc_vm_t *vm, size_t nargs)
uint64_t intval;
uc_value_t *rv;
if (!iter)
err_return(EINVAL, NULL);
if (!iter->has_next)
return NULL;
if (iter->key_size == 4)
intval = *(uint32_t *)iter->key;
else if (iter->key_size == 8)
intval = *(uint64_t *)iter->key;
else
if (iter->key_size == 4) {
uint32_t val32;
memcpy(&val32, iter->key, sizeof(val32));
intval = val32;
} else if (iter->key_size == 8) {
memcpy(&intval, iter->key, sizeof(intval));
} else {
return NULL;
}
rv = ucv_int64_new(intval);
iter->has_next = !bpf_map_get_next_key(iter->fd, &iter->key, &iter->key);
@@ -561,6 +790,9 @@ uc_bpf_map_foreach(uc_vm_t *vm, size_t nargs)
void *key, *next;
bool ret = false;
if (!map)
err_return(EINVAL, NULL);
key = alloca(map->key_size);
next = alloca(map->key_size);
has_next = !bpf_map_get_next_key(map->fd.fd, NULL, next);
@@ -591,12 +823,250 @@ uc_bpf_map_foreach(uc_vm_t *vm, size_t nargs)
return ucv_boolean_new(ret);
}
enum {
UC_BPF_BUF_CB,
UC_BPF_BUF_LOST_CB,
UC_BPF_BUF_MAP,
__UC_BPF_BUF_MAX
};
struct uc_bpf_buffer {
uc_vm_t *vm;
uc_value_t *res;
bool perf;
bool exception;
union {
struct ring_buffer *rb;
struct perf_buffer *pb;
};
};
static uc_value_t *
uc_bpf_buffer_call(struct uc_bpf_buffer *buf, size_t slot, uc_value_t *arg,
int cpu)
{
uc_vm_t *vm = buf->vm;
size_t fn_nargs = 1;
uc_vm_stack_push(vm, ucv_get(ucv_resource_value_get(buf->res, slot)));
uc_vm_stack_push(vm, arg);
if (cpu >= 0) {
uc_vm_stack_push(vm, ucv_int64_new(cpu));
fn_nargs++;
}
if (uc_vm_call(vm, false, fn_nargs) != EXCEPTION_NONE) {
buf->exception = true;
return NULL;
}
return uc_vm_stack_pop(vm);
}
static int
uc_bpf_ringbuf_sample(void *ctx, void *data, size_t size)
{
struct uc_bpf_buffer *buf = ctx;
uc_value_t *rv;
int ret = 0;
if (buf->exception)
return -1;
rv = uc_bpf_buffer_call(buf, UC_BPF_BUF_CB,
ucv_string_new_length(data, size), -1);
if (buf->exception)
return -1;
if (ucv_type(rv) == UC_INTEGER)
ret = ucv_int64_get(rv);
ucv_put(rv);
return ret;
}
static void
uc_bpf_perf_sample(void *ctx, int cpu, void *data, __u32 size)
{
struct uc_bpf_buffer *buf = ctx;
if (buf->exception)
return;
ucv_put(uc_bpf_buffer_call(buf, UC_BPF_BUF_CB,
ucv_string_new_length(data, size), cpu));
}
static void
uc_bpf_perf_lost(void *ctx, int cpu, __u64 cnt)
{
struct uc_bpf_buffer *buf = ctx;
if (buf->exception || !ucv_resource_value_get(buf->res, UC_BPF_BUF_LOST_CB))
return;
ucv_put(uc_bpf_buffer_call(buf, UC_BPF_BUF_LOST_CB, ucv_int64_new(cnt),
cpu));
}
static uc_value_t *
uc_bpf_map_buffer_create(uc_vm_t *vm, struct uc_bpf_map *map, bool perf,
size_t pages, uc_value_t *cb, uc_value_t *lost_cb)
{
struct uc_bpf_buffer *buf;
uc_value_t *res;
res = ucv_resource_create_ex(vm, "bpf.buffer", (void **)&buf,
__UC_BPF_BUF_MAX, sizeof(*buf));
buf->vm = vm;
buf->res = res;
buf->perf = perf;
if (perf)
buf->pb = perf_buffer__new(map->fd.fd, pages, uc_bpf_perf_sample,
uc_bpf_perf_lost, buf, NULL);
else
buf->rb = ring_buffer__new(map->fd.fd, uc_bpf_ringbuf_sample,
buf, NULL);
if (!buf->rb) {
ucv_put(res);
err_return(errno, NULL);
}
ucv_resource_value_set(res, UC_BPF_BUF_CB, ucv_get(cb));
ucv_resource_value_set(res, UC_BPF_BUF_LOST_CB, ucv_get(lost_cb));
ucv_resource_value_set(res, UC_BPF_BUF_MAP, ucv_get(_uc_fn_this_res(vm)));
return res;
}
static uc_value_t *
uc_bpf_map_ringbuf(uc_vm_t *vm, size_t nargs)
{
struct uc_bpf_map *map = uc_fn_thisval("bpf.map");
uc_value_t *cb = uc_fn_arg(0);
if (!map)
err_return(EINVAL, NULL);
if (map->type != BPF_MAP_TYPE_RINGBUF)
err_return(EINVAL, "map type");
if (!ucv_is_callable(cb))
err_return(EINVAL, "callback");
return uc_bpf_map_buffer_create(vm, map, false, 0, cb, NULL);
}
static uc_value_t *
uc_bpf_map_perf_buffer(uc_vm_t *vm, size_t nargs)
{
struct uc_bpf_map *map = uc_fn_thisval("bpf.map");
uc_value_t *cb = uc_fn_arg(0);
uc_value_t *a_pages = uc_fn_arg(1);
uc_value_t *lost_cb = uc_fn_arg(2);
size_t pages = 8;
if (!map)
err_return(EINVAL, NULL);
if (map->type != BPF_MAP_TYPE_PERF_EVENT_ARRAY)
err_return(EINVAL, "map type");
if (!ucv_is_callable(cb))
err_return(EINVAL, "callback");
if (a_pages) {
if (ucv_type(a_pages) != UC_INTEGER || ucv_int64_get(a_pages) <= 0)
err_return(EINVAL, "page count");
pages = ucv_int64_get(a_pages);
}
if (lost_cb && !ucv_is_callable(lost_cb))
err_return(EINVAL, "lost callback");
return uc_bpf_map_buffer_create(vm, map, true, pages, cb, lost_cb);
}
static uc_value_t *
uc_bpf_buffer_fileno(uc_vm_t *vm, size_t nargs)
{
struct uc_bpf_buffer *buf = uc_fn_thisval("bpf.buffer");
int fd;
if (!buf)
err_return(EINVAL, NULL);
fd = buf->perf ? perf_buffer__epoll_fd(buf->pb) :
ring_buffer__epoll_fd(buf->rb);
if (fd < 0)
err_return(errno, NULL);
return ucv_int64_new(fd);
}
static uc_value_t *
uc_bpf_buffer_result(uc_vm_t *vm, struct uc_bpf_buffer *buf, int ret)
{
if (buf->exception) {
buf->exception = false;
return NULL;
}
if (ret < 0)
err_return(errno, NULL);
return ucv_int64_new(ret);
}
static uc_value_t *
uc_bpf_buffer_poll(uc_vm_t *vm, size_t nargs)
{
struct uc_bpf_buffer *buf = uc_fn_thisval("bpf.buffer");
uc_value_t *a_timeout = uc_fn_arg(0);
int timeout = 0;
int ret;
if (!buf)
err_return(EINVAL, NULL);
if (a_timeout) {
if (ucv_type(a_timeout) != UC_INTEGER)
err_return(EINVAL, "timeout");
timeout = ucv_int64_get(a_timeout);
}
ret = buf->perf ? perf_buffer__poll(buf->pb, timeout) :
ring_buffer__poll(buf->rb, timeout);
return uc_bpf_buffer_result(vm, buf, ret);
}
static uc_value_t *
uc_bpf_buffer_consume(uc_vm_t *vm, size_t nargs)
{
struct uc_bpf_buffer *buf = uc_fn_thisval("bpf.buffer");
int ret;
if (!buf)
err_return(EINVAL, NULL);
ret = buf->perf ? perf_buffer__consume(buf->pb) :
ring_buffer__consume(buf->rb);
return uc_bpf_buffer_result(vm, buf, ret);
}
static uc_value_t *
uc_bpf_obj_pin(uc_vm_t *vm, size_t nargs, const char *type)
{
struct uc_bpf_fd *f = uc_fn_thisval(type);
uc_value_t *path = uc_fn_arg(0);
if (!f)
err_return(EINVAL, NULL);
if (ucv_type(path) != UC_STRING)
err_return(EINVAL, NULL);
@@ -665,9 +1135,7 @@ out:
return TRUE;
error:
if (fd >= 0)
err_return(ENOENT, NULL);
return NULL;
err_return(errno, NULL);
}
static uc_value_t *
@@ -752,6 +1220,23 @@ register_constants(uc_vm_t *vm, uc_value_t *scope)
ADD_CONST(BPF_PROG_TYPE_SCHED_CLS);
ADD_CONST(BPF_PROG_TYPE_SCHED_ACT);
ADD_CONST(BPF_MAP_TYPE_HASH);
ADD_CONST(BPF_MAP_TYPE_ARRAY);
ADD_CONST(BPF_MAP_TYPE_PROG_ARRAY);
ADD_CONST(BPF_MAP_TYPE_PERF_EVENT_ARRAY);
ADD_CONST(BPF_MAP_TYPE_PERCPU_HASH);
ADD_CONST(BPF_MAP_TYPE_PERCPU_ARRAY);
ADD_CONST(BPF_MAP_TYPE_LRU_HASH);
ADD_CONST(BPF_MAP_TYPE_LRU_PERCPU_HASH);
ADD_CONST(BPF_MAP_TYPE_LPM_TRIE);
ADD_CONST(BPF_MAP_TYPE_ARRAY_OF_MAPS);
ADD_CONST(BPF_MAP_TYPE_HASH_OF_MAPS);
ADD_CONST(BPF_MAP_TYPE_DEVMAP);
ADD_CONST(BPF_MAP_TYPE_SOCKMAP);
ADD_CONST(BPF_MAP_TYPE_QUEUE);
ADD_CONST(BPF_MAP_TYPE_STACK);
ADD_CONST(BPF_MAP_TYPE_RINGBUF);
ADD_CONST(BPF_ANY);
ADD_CONST(BPF_NOEXIST);
ADD_CONST(BPF_EXIST);
@@ -774,12 +1259,31 @@ static void module_free(void *ptr)
static const uc_function_list_t map_fns[] = {
{ "pin", uc_bpf_map_pin },
{ "info", uc_bpf_map_info },
{ "get", uc_bpf_map_get },
{ "set", uc_bpf_map_set },
{ "delete", uc_bpf_map_delete },
{ "delete_all", uc_bpf_map_delete_all },
{ "foreach", uc_bpf_map_foreach },
{ "iterator", uc_bpf_map_iterator },
{ "ringbuf", uc_bpf_map_ringbuf },
{ "perf_buffer", uc_bpf_map_perf_buffer },
};
static void uc_bpf_buffer_free(void *ptr)
{
struct uc_bpf_buffer *buf = ptr;
if (buf->perf)
perf_buffer__free(buf->pb);
else
ring_buffer__free(buf->rb);
}
static const uc_function_list_t buffer_fns[] = {
{ "fileno", uc_bpf_buffer_fileno },
{ "poll", uc_bpf_buffer_poll },
{ "consume", uc_bpf_buffer_consume },
};
static void uc_bpf_fd_free(void *ptr)
@@ -805,7 +1309,9 @@ static const uc_function_list_t global_fns[] = {
{ "set_debug_handler", uc_bpf_set_debug_handler },
{ "open_module", uc_bpf_open_module },
{ "open_map", uc_bpf_open_map },
{ "open_map_id", uc_bpf_open_map_id },
{ "open_program", uc_bpf_open_program },
{ "open_program_id", uc_bpf_open_program_id },
{ "tc_detach", uc_bpf_tc_detach },
};
@@ -820,5 +1326,6 @@ void uc_module_init(uc_vm_t *vm, uc_value_t *scope)
uc_type_declare(vm, "bpf.module", module_fns, module_free);
uc_type_declare(vm, "bpf.map", map_fns, uc_bpf_fd_free);
uc_type_declare(vm, "bpf.map_iter", map_iter_fns, NULL);
uc_type_declare(vm, "bpf.buffer", buffer_fns, uc_bpf_buffer_free);
uc_type_declare(vm, "bpf.program", prog_fns, uc_bpf_fd_free);
}
+3 -3
View File
@@ -12,9 +12,9 @@ PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL=https://github.com/jow-/ucode.git
PKG_SOURCE_DATE:=2026-06-03
PKG_SOURCE_VERSION:=3ec4e5c238353067e4b58fb9bb9938d85d59e7c2
PKG_MIRROR_HASH:=bc0ccef8b34defd524705c73982b39ba9e86fab588e531eba1915eecc57a8351
PKG_SOURCE_DATE:=2026-07-09
PKG_SOURCE_VERSION:=b885dd0fe1e974551fb1233ca5f3aa74d80b74d9
PKG_MIRROR_HASH:=654ba9dc8714aa8caf75ef422962fb465c2b21c8fbe4518a47f9de391cce6e71
PKG_MAINTAINER:=Jo-Philipp Wich <jo@mein.io>
PKG_LICENSE:=ISC
@@ -1,80 +0,0 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Mon, 6 Jul 2026 18:23:08 +0200
Subject: [PATCH] compiler: scope named function expression names to their own
body
A named function expression (e.g. `!function e(){...}()`) shares its parsing
path with function declarations, and declared its name in the enclosing scope
just as a declaration does. For an expression this is wrong: the name is only
meant to be visible inside its own body for self-reference. Binding it in the
enclosing scope leaked the name and, by pushing an extra local, shifted the
slot count so the initialize_local for a let/const initialised by the
expression marked the wrong slot -- the real variable stayed uninitialised and
its first use raised "Can't access lexical declaration 'x' before
initialization". Self-recursion was likewise broken.
Bind the name in the enclosing scope only for declarations. For a named
expression, rename the function's own callee slot to the name after
initialising its compiler, so the body can reference itself while the enclosing
scope is left untouched.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/compiler.c
+++ b/compiler.c
@@ -1949,20 +1949,27 @@ uc_compiler_compile_funcexpr_common(uc_c
return;
}
- slot = uc_compiler_resolve_funcstub(compiler, name);
+ /* A function declaration binds its name in the enclosing scope. A named
+ * function expression must not: its name is only visible inside its own
+ * body (for self-reference), and declaring it in the enclosing scope
+ * would both leak it and shift the local slots, corrupting the
+ * initialisation state of a `let`/`const` the expression initialises. */
+ if (require_name) {
+ slot = uc_compiler_resolve_funcstub(compiler, name);
- if (slot > -1) {
- compiler->locals.entries[slot].funcstub = false;
- }
- else {
- slot = uc_compiler_declare_local(compiler, name, false);
+ if (slot > -1) {
+ compiler->locals.entries[slot].funcstub = false;
+ }
+ else {
+ slot = uc_compiler_declare_local(compiler, name, false);
- if (slot == -1)
- uc_compiler_initialize_local(compiler);
- else if (compiler->locals.entries[slot].constant)
- uc_compiler_syntax_error(compiler, compiler->parser->prev.pos,
- "Redeclaration of constant '%s'",
- ucv_string_get(name));
+ if (slot == -1)
+ uc_compiler_initialize_local(compiler);
+ else if (compiler->locals.entries[slot].constant)
+ uc_compiler_syntax_error(compiler, compiler->parser->prev.pos,
+ "Redeclaration of constant '%s'",
+ ucv_string_get(name));
+ }
}
}
else if (require_name) {
@@ -1976,6 +1983,14 @@ uc_compiler_compile_funcexpr_common(uc_c
compiler->program,
uc_compiler_is_strict(compiler));
+ /* Bind a named function expression's name to its own callee slot so the
+ * body can reference itself for recursion, without touching the enclosing
+ * scope. Declarations keep the name in the enclosing scope (handled above). */
+ if (name && !require_name) {
+ ucv_put(fncompiler.locals.entries[0].name);
+ fncompiler.locals.entries[0].name = ucv_get(name);
+ }
+
fncompiler.parent = compiler;
fncompiler.parser = compiler->parser;
fncompiler.exprstack = compiler->exprstack;
@@ -1,31 +0,0 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Tue, 7 Jul 2026 09:34:16 +0200
Subject: [PATCH] lib: avoid allocating print buffer before NULL check in
uc_error_message_indent
Move the print buffer allocation after the guard that returns early on a
NULL message, so no buffer is allocated on that path. This keeps the
function leak-free when callers invoke it without having produced an error
string.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/lib.c
+++ b/lib.c
@@ -220,13 +220,14 @@ uc_error_context_format(uc_stringbuf_t *
void
uc_error_message_indent(char **msg) {
- uc_stringbuf_t *buf = xprintbuf_new();
+ uc_stringbuf_t *buf;
char *s, *p, *nl;
size_t len;
if (!msg || !*msg)
return;
+ buf = xprintbuf_new();
s = *msg;
len = strlen(s);
@@ -1,53 +0,0 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Mon, 6 Jul 2026 11:12:32 +0200
Subject: [PATCH] compiler: prevent unbounded recompilation on failed
module imports
A source's exports.offset of -1 marks a module whose compilation has been
entered but not completed. When a module failed to compile, uc_compiler_finish
freed its function and the error cascaded up through the importers via error
recovery, leaving each involved source at offset -1 with its function gone. The
re-entry guard only recognised an in-progress module by finding its function, so
those freed modules were recompiled again on every subsequent import. Across a
wide import graph this recompilation multiplies and the compiler appears to hang.
Detect the offset -1 marker directly instead of relying on the presence of a
function: report a circular dependency when the function is still around, and
propagate the failure otherwise, rather than recursing into the module again.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/compiler.c
+++ b/compiler.c
@@ -3479,15 +3479,26 @@ uc_compiler_compile_module_source(uc_com
uc_program_function_foreach(compiler->program, fn) {
if (uc_program_function_source(fn) == source) {
- if (source->exports.offset == (size_t)-1)
- uc_compiler_syntax_error(compiler, compiler->parser->prev.pos,
- "Circular dependency");
-
loaded = true;
break;
}
}
+ /* An offset of -1 marks a source whose compilation was entered but not yet
+ * completed. Re-entering it is either a circular dependency (its function is
+ * still present) or a module whose earlier compilation failed and freed its
+ * function. Recompiling in either case recurses without bound across a large
+ * import graph, so report the cycle and propagate the failure instead. */
+ if (source->exports.offset == (size_t)-1) {
+ if (loaded)
+ uc_compiler_syntax_error(compiler, compiler->parser->prev.pos,
+ "Circular dependency");
+ else if (errp)
+ xasprintf(errp, "Module previously failed to compile\n");
+
+ return false;
+ }
+
if (!loaded) {
/* We do not yet support linking precompiled modules at compile time,
turn static import operation into dynamic load one. */
@@ -11,7 +11,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
--- a/lib/uloop.c
+++ b/lib/uloop.c
@@ -1023,7 +1023,22 @@ uc_uloop_process(uc_vm_t *vm, size_t nar
@@ -1028,7 +1028,22 @@ uc_uloop_process(uc_vm_t *vm, size_t nar
if (pid == 0) {
argp = calloc(ucv_array_length(arguments) + 2, sizeof(char *));
@@ -35,7 +35,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
if (!argp || !envp)
_exit(-1);
@@ -1033,19 +1048,6 @@ uc_uloop_process(uc_vm_t *vm, size_t nar
@@ -1038,19 +1053,6 @@ uc_uloop_process(uc_vm_t *vm, size_t nar
for (i = 0; i < ucv_array_length(arguments); i++)
argp[i+1] = ucv_to_string(vm, ucv_array_get(arguments, i));
@@ -10,7 +10,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
--- a/lib/uloop.c
+++ b/lib/uloop.c
@@ -968,8 +968,9 @@ uc_uloop_process_cb(struct uloop_process
@@ -973,8 +973,9 @@ uc_uloop_process_cb(struct uloop_process
*
* This function creates a process instance for executing external programs.
* It takes the executable path string, an optional string array as the argument
@@ -22,7 +22,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
*
* @function module:uloop#process
*
@@ -986,6 +987,11 @@ uc_uloop_process_cb(struct uloop_process
@@ -991,6 +992,11 @@ uc_uloop_process_cb(struct uloop_process
* @param {Function} callback
* The callback function to be invoked when the invoked process ends.
*
@@ -34,7 +34,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
* @returns {?module:uloop.process}
* Returns a process instance for executing external programs.
* Returns `null` on error, e.g. due to `exec()` failure or invalid arguments.
@@ -995,6 +1001,16 @@ uc_uloop_process_cb(struct uloop_process
@@ -1000,6 +1006,16 @@ uc_uloop_process_cb(struct uloop_process
* const myProcess = uloop.process("/bin/ls", ["-l", "/tmp"], null, (code) => {
* printf(`Process exited with code ${code}\n`);
* });
@@ -51,7 +51,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
*/
static uc_value_t *
uc_uloop_process(uc_vm_t *vm, size_t nargs)
@@ -1003,6 +1019,7 @@ uc_uloop_process(uc_vm_t *vm, size_t nar
@@ -1008,6 +1024,7 @@ uc_uloop_process(uc_vm_t *vm, size_t nar
uc_value_t *arguments = uc_fn_arg(1);
uc_value_t *env_arg = uc_fn_arg(2);
uc_value_t *callback = uc_fn_arg(3);
@@ -59,7 +59,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
uc_uloop_process_t *process;
uc_stringbuf_t *buf;
char **argp, **envp;
@@ -1012,7 +1029,8 @@ uc_uloop_process(uc_vm_t *vm, size_t nar
@@ -1017,7 +1034,8 @@ uc_uloop_process(uc_vm_t *vm, size_t nar
if (ucv_type(executable) != UC_STRING ||
(arguments && ucv_type(arguments) != UC_ARRAY) ||
(env_arg && ucv_type(env_arg) != UC_OBJECT) ||
@@ -69,7 +69,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
err_return(EINVAL);
}
@@ -1022,6 +1040,13 @@ uc_uloop_process(uc_vm_t *vm, size_t nar
@@ -1027,6 +1045,13 @@ uc_uloop_process(uc_vm_t *vm, size_t nar
err_return(errno);
if (pid == 0) {
@@ -0,0 +1,21 @@
#
# Copyright (C) 2015 OpenWrt.org
#
. /lib/functions/uci-defaults.sh
board_config_update
board=$(board_name)
case "$board" in
nokia,xg-040g-mf)
ucidef_set_led_netdev "lan2" "lan2" "mt7530-0:0a:green:lan-2" "lan2" "link tx rx"
ucidef_set_led_netdev "lan3" "lan3" "mt7530-0:0b:green:lan-3" "lan3" "link tx rx"
ucidef_set_led_netdev "lan4" "lan4" "mt7530-0:0c:green:lan-4" "lan4" "link tx rx"
;;
esac
board_config_flush
exit 0
@@ -14,6 +14,9 @@ an7583_setup_interfaces()
airoha,an7583-evb)
ucidef_set_interface_lan "lan1 lan2 lan3 lan4 eth1"
;;
nokia,xg-040g-mf)
ucidef_set_interface_lan "lan1 lan2 lan3 lan4"
;;
*)
echo "Unsupported hardware. Network interfaces not initialized"
;;
@@ -0,0 +1,45 @@
RAMFS_COPY_BIN='fitblk fit_check_sign'
REQUIRE_IMAGE_METADATA=1
nokia_initial_setup()
{
[ "$(rootfs_type)" = "tmpfs" ] || return 0
fw_setenv bootcmd "flash read 0xc0000 0x800000 0x85000000; bootm 0x85000000"
}
platform_check_image() {
local board=$(board_name)
case "$board" in
*)
nand_do_platform_check "$board" "$1"
return $?
;;
esac
return 0
}
platform_do_upgrade() {
local board=$(board_name)
case "$board" in
*)
nand_do_upgrade "$1"
;;
esac
}
platform_pre_upgrade() {
local board=$(board_name)
case "$board" in
nokia,xg-040g-mf)
nokia_initial_setup
;;
*)
;;
esac
}
+37 -28
View File
@@ -1,11 +1,8 @@
CONFIG_64BIT=y
CONFIG_AIROHA_CPU_PM_DOMAIN=y
CONFIG_AIROHA_SCU_SSR=y
CONFIG_AIROHA_THERMAL=y
CONFIG_AIROHA_WATCHDOG=y
CONFIG_ARCH_AIROHA=y
CONFIG_ARCH_BINFMT_ELF_EXTRA_PHDRS=y
CONFIG_ARCH_CORRECT_STACKTRACE_ON_KRETPROBE=y
CONFIG_ARCH_DEFAULT_KEXEC_IMAGE_VERIFY_SIG=y
CONFIG_ARCH_DMA_ADDR_T_64BIT=y
CONFIG_ARCH_FORCE_MAX_ORDER=10
@@ -15,41 +12,41 @@ CONFIG_ARCH_MMAP_RND_BITS=18
CONFIG_ARCH_MMAP_RND_BITS_MAX=24
CONFIG_ARCH_MMAP_RND_BITS_MIN=18
CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=11
CONFIG_ARCH_PKEY_BITS=3
CONFIG_ARCH_PROC_KCORE_TEXT=y
CONFIG_ARCH_SPARSEMEM_ENABLE=y
CONFIG_ARCH_STACKWALK=y
CONFIG_ARCH_SUSPEND_POSSIBLE=y
CONFIG_ARCH_WANTS_EXECMEM_LATE=y
CONFIG_ARCH_WANTS_NO_INSTR=y
CONFIG_ARCH_WANTS_THP_SWAP=y
CONFIG_ARM64=y
CONFIG_ARM64_4K_PAGES=y
CONFIG_ARM64_BRBE=y
CONFIG_ARM64_ERRATUM_843419=y
CONFIG_ARM64_LD_HAS_FIX_ERRATUM_843419=y
CONFIG_ARM64_PA_BITS=48
CONFIG_ARM64_PA_BITS_48=y
CONFIG_ARM64_PLATFORM_DEVICES=y
CONFIG_ARM64_TAGGED_ADDR_ABI=y
CONFIG_ARM64_VA_BITS=39
CONFIG_ARM64_VA_BITS_39=y
# CONFIG_ARM64_VA_BITS_48 is not set
# CONFIG_ARM64_VA_BITS_52 is not set
CONFIG_ARM_AIROHA_SOC_CPUFREQ=y
CONFIG_ARM_AMBA=y
CONFIG_ARM_ARCH_TIMER=y
CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y
# CONFIG_ARM_DEBUG_WX is not set
CONFIG_ARM_GIC=y
CONFIG_ARM_GIC_ITS_PARENT=y
CONFIG_ARM_GIC_V2M=y
CONFIG_ARM_GIC_V3=y
CONFIG_ARM_GIC_V3_ITS=y
CONFIG_ARM_GIC_V5=y
CONFIG_ARM_PMU=y
CONFIG_ARM_PMUV3=y
CONFIG_ARM_PSCI_FW=y
CONFIG_ARM_SMCCC_SOC_ID=y
# CONFIG_ARM_SMMU is not set
# CONFIG_ARM_SMMU_V3 is not set
CONFIG_AUDIT_ARCH_COMPAT_GENERIC=y
CONFIG_BLK_PM=y
CONFIG_BLOCK_NOTIFIERS=y
CONFIG_BUFFER_HEAD=y
CONFIG_BUILTIN_RETURN_ADDRESS_STRIPS_PAC=y
CONFIG_CC_HAVE_SHADOW_CALL_STACK=y
@@ -59,7 +56,6 @@ CONFIG_COMMON_CLK=y
CONFIG_COMMON_CLK_EN7523=y
CONFIG_COMPACT_UNEVICTABLE_DEFAULT=1
# CONFIG_COMPAT_32BIT_TIME is not set
# CONFIG_COMPRESSED_INSTALL is not set
CONFIG_CONTEXT_TRACKING=y
CONFIG_CONTEXT_TRACKING_IDLE=y
CONFIG_CPUFREQ_DT=y
@@ -76,31 +72,35 @@ CONFIG_CPU_FREQ_GOV_POWERSAVE=y
CONFIG_CPU_FREQ_GOV_USERSPACE=y
CONFIG_CPU_FREQ_STAT=y
CONFIG_CPU_LITTLE_ENDIAN=y
CONFIG_CPU_MITIGATIONS=y
CONFIG_CPU_RMAP=y
CONFIG_CRC16=y
CONFIG_CRC32_ARCH=y
CONFIG_CRC_CCITT=y
CONFIG_CRYPTO_CRC32C=y
CONFIG_CRYPTO_DEFLATE=y
CONFIG_CRYPTO_DEV_EIP93=y
CONFIG_CRYPTO_ECB=y
CONFIG_CRYPTO_HASH_INFO=y
CONFIG_CRYPTO_LIB_BLAKE2S_ARCH=y
CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y
CONFIG_CRYPTO_LIB_GF128MUL=y
CONFIG_CRYPTO_LIB_SHA1=y
CONFIG_CRYPTO_LIB_SHA1_ARCH=y
CONFIG_CRYPTO_LIB_SHA256_ARCH=y
CONFIG_CRYPTO_LIB_UTILS=y
CONFIG_CRYPTO_LZO=y
CONFIG_CRYPTO_ZSTD=y
CONFIG_DCACHE_WORD_ACCESS=y
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_MISC=y
CONFIG_DEV_COREDUMP=y
CONFIG_DMADEVICES=y
CONFIG_DMA_BOUNCE_UNALIGNED_KMALLOC=y
CONFIG_DMA_DIRECT_REMAP=y
CONFIG_DMA_ENGINE=y
CONFIG_DMA_NEED_SYNC=y
CONFIG_DMA_OF=y
CONFIG_DMA_OPS_HELPERS=y
CONFIG_DTC=y
CONFIG_EDAC_SUPPORT=y
CONFIG_EXCLUSIVE_SYSTEM_RAM=y
CONFIG_EXT4_FS=y
CONFIG_FIXED_PHY=y
CONFIG_FIX_EARLYCON_MEM=y
@@ -111,7 +111,6 @@ CONFIG_FUNCTION_ALIGNMENT=4
CONFIG_FUNCTION_ALIGNMENT_4B=y
CONFIG_FWNODE_MDIO=y
CONFIG_FWNODE_PCS=y
CONFIG_FW_CACHE=y
# CONFIG_FW_LOADER_USER_HELPER is not set
CONFIG_GCC_SUPPORTS_DYNAMIC_FTRACE_WITH_ARGS=y
CONFIG_GENERIC_ALLOCATOR=y
@@ -129,6 +128,9 @@ CONFIG_GENERIC_GETTIMEOFDAY=y
CONFIG_GENERIC_IDLE_POLL_SETUP=y
CONFIG_GENERIC_IOREMAP=y
CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y
CONFIG_GENERIC_IRQ_ENTRY=y
CONFIG_GENERIC_IRQ_KEXEC_CLEAR_VM_FORWARD=y
CONFIG_GENERIC_IRQ_MIGRATION=y
CONFIG_GENERIC_IRQ_SHOW=y
CONFIG_GENERIC_IRQ_SHOW_LEVEL=y
CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED=y
@@ -154,16 +156,16 @@ CONFIG_HAS_DMA=y
CONFIG_HAS_IOMEM=y
CONFIG_HAS_IOPORT=y
CONFIG_HAS_IOPORT_MAP=y
# CONFIG_HISILICON_ERRATUM_162100801 is not set
CONFIG_HOTPLUG_CORE_SYNC=y
CONFIG_HOTPLUG_CORE_SYNC_DEAD=y
CONFIG_HOTPLUG_CPU=y
CONFIG_HOTPLUG_SMT=y
CONFIG_HW_RANDOM=y
CONFIG_HW_RANDOM_AIROHA=y
# CONFIG_IDPF is not set
CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000
CONFIG_INITRAMFS_SOURCE=""
CONFIG_IO_URING=y
CONFIG_IPC_NS=y
CONFIG_IO_URING_ZCRX=y
CONFIG_IPV6=y
CONFIG_IPV6_MULTIPLE_TABLES=y
# CONFIG_IPV6_SUBTREES is not set
@@ -180,6 +182,7 @@ CONFIG_IRQCHIP=y
CONFIG_IRQ_DOMAIN=y
CONFIG_IRQ_DOMAIN_HIERARCHY=y
CONFIG_IRQ_FORCED_THREADING=y
CONFIG_IRQ_MSI_IOMMU=y
CONFIG_IRQ_MSI_LIB=y
CONFIG_IRQ_WORK=y
CONFIG_JBD2=y
@@ -191,15 +194,13 @@ CONFIG_LZO_COMPRESS=y
CONFIG_LZO_DECOMPRESS=y
CONFIG_MDIO_AIROHA=y
CONFIG_MDIO_BUS=y
CONFIG_MDIO_DEVICE=y
CONFIG_MDIO_DEVRES=y
# CONFIG_MEDIATEK_GE_SOC_PHY is not set
# CONFIG_MEMCG is not set
CONFIG_MEDIATEK_GE_SOC_PHY=y
CONFIG_MFD_SYSCON=y
CONFIG_MIGRATION=y
CONFIG_MMC=y
CONFIG_MMC_BLOCK=y
CONFIG_MMC_CQHCI=y
CONFIG_MMC_HSQ=y
CONFIG_MMC_MTK=y
CONFIG_MMU_LAZY_TLB_REFCOUNT=y
CONFIG_MODULES_TREE_LOOKUP=y
@@ -215,6 +216,8 @@ CONFIG_MTD_UBI=y
CONFIG_MTD_UBI_BEB_LIMIT=20
CONFIG_MTD_UBI_BLOCK=y
CONFIG_MTD_UBI_WL_THRESHOLD=4096
CONFIG_MTD_VIRT_CONCAT=y
CONFIG_MTK_NET_PHYLIB=y
CONFIG_MUTEX_SPIN_ON_OWNER=y
CONFIG_NEED_DMA_MAP_STATE=y
CONFIG_NEED_SG_DMA_LENGTH=y
@@ -224,15 +227,17 @@ CONFIG_NET_AIROHA_NPU=y
CONFIG_NET_DEVLINK=y
CONFIG_NET_DSA=y
CONFIG_NET_DSA_MT7530=y
CONFIG_NET_DSA_MT7530_MDIO=y
# CONFIG_NET_DSA_MT7530_MDIO is not set
CONFIG_NET_DSA_MT7530_MMIO=y
CONFIG_NET_DSA_TAG_MTK=y
CONFIG_NET_EGRESS=y
CONFIG_NET_FLOW_LIMIT=y
# CONFIG_NET_MEDIATEK_SOC is not set
CONFIG_NET_INGRESS=y
CONFIG_NET_SELFTESTS=y
# CONFIG_NET_VENDOR_3COM is not set
CONFIG_NET_VENDOR_AIROHA=y
# CONFIG_NET_VENDOR_MEDIATEK is not set
CONFIG_NET_XGRESS=y
CONFIG_NLS=y
CONFIG_NO_HZ_COMMON=y
CONFIG_NO_HZ_IDLE=y
@@ -250,6 +255,7 @@ CONFIG_OF_GPIO=y
CONFIG_OF_IRQ=y
CONFIG_OF_KOBJ=y
CONFIG_OF_MDIO=y
CONFIG_PADATA=y
CONFIG_PAGE_BLOCK_MAX_ORDER=10
CONFIG_PAGE_POOL=y
CONFIG_PAGE_SIZE_LESS_THAN_256KB=y
@@ -269,6 +275,7 @@ CONFIG_PCIE_PME=y
CONFIG_PCI_DOMAINS=y
CONFIG_PCI_DOMAINS_GENERIC=y
CONFIG_PCI_MSI=y
CONFIG_PCS_AIROHA=y
# CONFIG_PCS_AIROHA_AN7581 is not set
CONFIG_PCS_AIROHA_AN7583=y
CONFIG_PERF_EVENTS=y
@@ -280,6 +287,7 @@ CONFIG_PHYLINK=y
CONFIG_PHYS_ADDR_T_64BIT=y
CONFIG_PHY_AIROHA_PCIE=y
# CONFIG_PHY_AIROHA_USB is not set
CONFIG_PHY_PACKAGE=y
CONFIG_PINCTRL=y
CONFIG_PINCTRL_AIROHA=y
# CONFIG_PINCTRL_MT2712 is not set
@@ -308,13 +316,13 @@ CONFIG_RANDSTRUCT_NONE=y
CONFIG_RAS=y
CONFIG_RATIONAL=y
CONFIG_REGMAP=y
CONFIG_REGMAP_IRQ=y
CONFIG_REGMAP_MMIO=y
CONFIG_REGULATOR=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
CONFIG_RELOCATABLE=y
CONFIG_RESET_CONTROLLER=y
CONFIG_RFS_ACCEL=y
CONFIG_RODATA_FULL_DEFAULT_ENABLED=y
CONFIG_RPS=y
CONFIG_RWSEM_SPIN_ON_OWNER=y
CONFIG_SECCOMP=y
@@ -327,8 +335,8 @@ CONFIG_SERIAL_8250_RUNTIME_UARTS=5
CONFIG_SERIAL_8250_SHARE_IRQ=y
CONFIG_SERIAL_MCTRL_GPIO=y
CONFIG_SERIAL_OF_PLATFORM=y
CONFIG_SGL_ALLOC=y
CONFIG_SMP=y
# CONFIG_SND_SOC_AN7581 is not set
CONFIG_SOCK_RX_QUEUE_MAPPING=y
CONFIG_SOC_BUS=y
CONFIG_SOFTIRQ_ON_OWN_STACK=y
@@ -338,14 +346,14 @@ CONFIG_SPARSEMEM_VMEMMAP=y
CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y
CONFIG_SPARSE_IRQ=y
CONFIG_SPI=y
# CONFIG_SPI_AIROHA_EN7523 is not set
CONFIG_SPI_AIROHA_SNFI=y
CONFIG_SPI_MASTER=y
CONFIG_SPI_MEM=y
CONFIG_SPLIT_PMD_PTLOCKS=y
CONFIG_SPLIT_PTE_PTLOCKS=y
CONFIG_SWIOTLB=y
CONFIG_SWPHY=y
CONFIG_SYSCTL_EXCEPTION_TRACE=y
# CONFIG_TEST_FPU is not set
CONFIG_THERMAL=y
CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y
CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0
@@ -363,6 +371,7 @@ CONFIG_UBIFS_FS=y
CONFIG_USER_STACKTRACE_SUPPORT=y
CONFIG_VDSO_GETRANDOM=y
CONFIG_VMAP_STACK=y
CONFIG_WANT_DEV_COREDUMP=y
CONFIG_WATCHDOG_CORE=y
# CONFIG_WLAN is not set
# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set
+5 -1
View File
@@ -3,7 +3,11 @@ SUBTARGET:=an7583
BOARDNAME:=AN7583
CPU_TYPE:=cortex-a53
KERNELNAME:=Image dtbs
FEATURES+=pwm source-only
FEATURES+=pwm
DEFAULT_PACKAGES += \
airoha-an7583-npu-firmware kmod-leds-gpio \
kmod-gpio-button-hotplug uboot-envtools
define Target/Description
Build firmware images for Airoha an7583 ARM based boards.
@@ -5,26 +5,9 @@
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/input/input.h>
#include "an7581.dtsi"
#include "an758x-nokia_xg-040g-common.dtsi"
/ {
aliases {
label-mac-device = &gdm1;
led-boot = &led_power_green;
led-failsafe = &led_power_green;
led-running = &led_power_green;
led-upgrade = &led_power_green;
serial0 = &uart1;
};
chosen {
bootargs = "console=ttyS0,115200 earlycon";
stdout-path = "serial0:115200n8";
linux,usable-memory-range =
<0x0 0x80200000 0x0 0x1fe00000>;
};
gpio-keys {
compatible = "gpio-keys";
@@ -83,11 +66,6 @@
};
};
memory@80000000 {
device_type = "memory";
reg = <0x0 0x80000000 0x0 0x20000000>;
};
reg_3p3v: regulator-3p3v {
compatible = "regulator-fixed";
regulator-always-on;
@@ -153,18 +131,6 @@
};
};
&eth {
status = "okay";
};
&eth_pcs {
status = "okay";
};
&i2c0 {
status = "okay";
};
&mdio {
/* Airoha EN8811 2.5Gbps phy */
en8811: ethernet-phy@f {
@@ -190,44 +156,6 @@
};
};
&npu {
status = "okay";
};
&pon_pcs {
status = "okay";
};
&snfi {
status = "okay";
};
/*
* SkyHigh ML02G300WHI00
*/
&spi_nand {
/*
* ECC must be aligned with bootloader to prevent read errors.
* Stock fw also uses NAND_ECC_NONE.
*/
nand-ecc-mode = "none";
nand-ecc-step-size = <2048>;
nand-ecc-strength = <0>;
partitions: partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
};
};
&gdm1 {
status = "okay";
nvmem-cell-names = "mac-address";
nvmem-cells = <&macaddr_factory_3e (0)>;
};
&switch {
status = "okay";
@@ -1,84 +1,10 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
#include "an7581-nokia_xg-040g-md-common.dtsi"
#include "an758x-nokia_xg-040g-ubi-parts.dtsi"
/ {
model = "Nokia XG-040G-MD (UBI)";
compatible = "nokia,xg-040g-md-ubi", "airoha,an7581",
"airoha,en7581";
chosen {
rootdisk = <&ubi_fit>;
};
};
&partitions {
partition@0_all {
label = "all_flash";
reg = <0x0 0x10000000>;
read-only;
};
partition@0 {
label = "bl2";
reg = <0x0 0x20000>;
read-only;
};
partition@20000 {
label = "ubi";
reg = <0x20000 0xffe0000>;
compatible = "linux,ubi";
volumes {
ubi-volume-bosa {
volname = "bosa";
};
ubi-volume-factory {
volname = "ri";
nvmem-layout {
compatible = "fixed-layout";
#address-cells = <1>;
#size-cells = <1>;
macaddr_factory_3e: macaddr@3e {
compatible = "mac-base";
reg = <0x3e 0x6>;
#nvmem-cell-cells = <1>;
};
};
};
ubi_fit: ubi-volume-fit {
volname = "fit";
};
ubi-volume-fip {
volname = "fip";
};
ubi_env: ubi-volume-ubootenv {
volname = "ubootenv";
};
ubi_env2: ubi-volume-ubootenv2 {
volname = "ubootenv2";
};
};
};
};
&ubi_env {
nvmem-layout {
compatible = "u-boot,env-redundant-bool-layout";
};
};
&ubi_env2 {
nvmem-layout {
compatible = "u-boot,env-redundant-bool-layout";
};
};
@@ -1,152 +1,9 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
#include "an7581-nokia_xg-040g-md-common.dtsi"
#include "an758x-nokia_xg-040g-stock-parts.dtsi"
/ {
model = "Nokia XG-040G-MD";
compatible = "nokia,xg-040g-md", "airoha,an7581", "airoha,en7581";
virtual_flash {
compatible = "mtd-concat";
devices = <&ubi4 &ubi1 &ubi2 &ubi3 &ubi5 &ubi6>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "ubi";
reg = <0x0 0x0>;
};
};
};
};
&partitions {
partition@0_all {
label = "all_flash";
reg = <0x0 0xeba0000>;
read-only;
};
partition@0 {
label = "bootloader";
reg = <0x0 0x80000>;
read-only;
};
partition@60000 {
label = "u-boot-env";
reg = <0x60000 0x20000>;
};
partition@80000 {
label = "romfile";
reg = <0x80000 0x40000>;
read-only;
};
partition@c0000 {
label = "nsb_1";
reg = <0xc0000 0x2880000>;
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "kernel";
reg = <0x0 0x800000>;
};
ubi1: partition@800000 {
label = "rootfs_1";
reg = <0x800000 0x2080000>;
};
};
ubi2: partition@2940000 {
label = "nsb_2";
reg = <0x2940000 0x2880000>;
};
partition@51c0000 {
label = "bosa";
reg = <0x51c0000 0x40000>;
read-only;
};
partition@5200000 {
label = "ri";
reg = <0x5200000 0x40000>;
read-only;
nvmem-layout {
compatible = "fixed-layout";
#address-cells = <1>;
#size-cells = <1>;
macaddr_factory_3e: macaddr@3e {
compatible = "mac-base";
reg = <0x3e 0x6>;
#nvmem-cell-cells = <1>;
};
};
};
partition@5240000 {
label = "flag";
reg = <0x5240000 0x40000>;
read-only;
};
partition@5280000 {
label = "flagback";
reg = <0x5280000 0x40000>;
read-only;
};
ubi3: partition@52c0000 {
label = "config";
reg = <0x52c0000 0xa00000>;
};
ubi4: partition@5cc0000 {
label = "data";
reg = <0x5cc0000 0x80e0000>;
};
ubi5: partition@dda0000 {
label = "oopsfs";
reg = <0xdda0000 0x400000>;
};
partition@e1a0000 {
label = "log";
reg = <0xe1a0000 0xa00000>;
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
ubi6: partition@0 {
label = "log_truncated";
reg = <0x0 0x9c0000>;
};
/*
* Must be excluded from ubi. Otherwise the data
* will be broken after U-Boot 'bootflag swap'
* command call. This command writes bootflag at
* 0xeb60000 (boot_flag_addr).
*/
partition@9c0000 {
label = "bootflag";
reg = <0x9c0000 0x40000>;
read-only;
};
};
};
@@ -0,0 +1,276 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/dts-v1/;
#include <dt-bindings/leds/common.h>
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/input/input.h>
#include "an7583.dtsi"
#include "an758x-nokia_xg-040g-common.dtsi"
/ {
gpio-keys {
compatible = "gpio-keys";
button-0 {
label = "reset";
linux,code = <KEY_RESTART>;
gpios = <&an7583_pinctrl 0 GPIO_ACTIVE_LOW>;
debounce-interval = <60>;
};
};
leds {
compatible = "gpio-leds";
led-0 {
color = <LED_COLOR_ID_GREEN>;
function = LED_FUNCTION_WAN;
gpios = <&an7583_pinctrl 6 GPIO_ACTIVE_LOW>;
};
led-1 {
color = <LED_COLOR_ID_GREEN>;
function = LED_FUNCTION_WAN_ONLINE;
gpios = <&an7583_pinctrl 7 GPIO_ACTIVE_LOW>;
};
led_power_green: led-2 {
color = <LED_COLOR_ID_GREEN>;
function = LED_FUNCTION_POWER;
gpios = <&an7583_pinctrl 25 GPIO_ACTIVE_LOW>;
panic-indicator;
};
led-3 {
color = <LED_COLOR_ID_GREEN>;
function = LED_FUNCTION_USB;
function-enumerator = <1>;
gpios = <&an7583_pinctrl 26 GPIO_ACTIVE_LOW>;
/*
* AN7583 SoC doesn't support USB at this time
*
* linux,default-trigger = "usbport";
* trigger-sources = <&usb_port1>;
*/
};
led-4 {
color = <LED_COLOR_ID_RED>;
function = LED_FUNCTION_WAN;
gpios = <&an7583_pinctrl 27 GPIO_ACTIVE_LOW>;
};
led-5 {
color = <LED_COLOR_ID_GREEN>;
function = LED_FUNCTION_USB;
function-enumerator = <2>;
gpios = <&an7583_pinctrl 33 GPIO_ACTIVE_LOW>;
/*
* AN7583 SoC doesn't support USB at this time
*
* linux,default-trigger = "usbport";
* trigger-sources = <&usb_port2>;
*/
};
};
/*
* AN7583 SoC doesn't support USB at this time
*
* reg_3p3v: regulator-3p3v {
* compatible = "regulator-fixed";
* regulator-always-on;
* regulator-boot-on;
* regulator-max-microvolt = <3300000>;
* regulator-min-microvolt = <3300000>;
* regulator-name = "fixed-3.3V";
* };
*
* reg_usb_5v: regulator-usb-5v {
* compatible = "regulator-fixed";
* enable-active-high;
* gpio = <&an7583_pinctrl 30 GPIO_ACTIVE_HIGH>;
* regulator-boot-on;
* regulator-max-microvolt = <5000000>;
* regulator-min-microvolt = <5000000>;
* regulator-name = "usb-5v";
* };
*
* *
* * Controls both USB ports at once
* *
* usb-power {
* compatible = "regulator-output";
* vout-supply = <&reg_usb_5v>;
* };
*/
};
&an7583_pinctrl {
gpio-ranges = <&an7583_pinctrl 0 2 53>;
mdio0_pins: mdio0-pins {
conf {
pins = "mdio_0";
output-high;
};
};
pcie0_rst_pins: pcie0-rst-pins {
conf {
pins = "pcie_reset0";
drive-open-drain = <1>;
};
};
pcie1_rst_pins: pcie1-rst-pins {
conf {
pins = "pcie_reset1";
drive-open-drain = <1>;
};
};
gswp1_led0_pins: gswp1-led0-pins {
mux {
function = "phy1_led0";
pins = "gpio1";
};
};
gswp2_led0_pins: gswp2-led0-pins {
mux {
function = "phy2_led0";
pins = "gpio2";
};
};
gswp3_led0_pins: gswp3-led0-pins {
mux {
function = "phy3_led0";
pins = "gpio3";
};
};
gswp4_led0_pins: gswp4-led0-pins {
mux {
function = "phy4_led0";
pins = "gpio4";
};
};
};
&mdio_0 {
/* Airoha EN8811 2.5Gbps phy */
en8811: ethernet-phy@f {
compatible = "ethernet-phy-ieee802.3-c45";
reg = <0xf>;
reset-gpios = <&an7583_pinctrl 34 GPIO_ACTIVE_LOW>;
reset-assert-us = <10000>;
reset-deassert-us = <20000>;
leds {
#address-cells = <1>;
#size-cells = <0>;
led-0 {
reg = <0>;
color = <LED_COLOR_ID_GREEN>;
function = LED_FUNCTION_LAN;
function-enumerator = <1>;
default-state = "keep";
};
};
};
};
&switch {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&mdio0_pins>;
};
&gsw_port1 {
status = "disabled";
};
&gsw_phy1 {
status = "disabled";
};
&gsw_phy2 {
interrupts = <2>;
pinctrl-names = "gbe-led";
pinctrl-0 = <&gswp2_led0_pins>;
};
&gsw_phy2_led0 {
status = "okay";
color = <LED_COLOR_ID_GREEN>;
active-low;
};
&gsw_phy3 {
interrupts = <3>;
pinctrl-names = "gbe-led";
pinctrl-0 = <&gswp3_led0_pins>;
};
&gsw_phy3_led0 {
status = "okay";
color = <LED_COLOR_ID_GREEN>;
active-low;
};
&gsw_phy4 {
interrupts = <4>;
pinctrl-names = "gbe-led";
pinctrl-0 = <&gswp4_led0_pins>;
};
&gsw_phy4_led0 {
status = "okay";
color = <LED_COLOR_ID_GREEN>;
active-low;
};
&gdm3 {
status = "okay";
openwrt,netdev-name = "lan1";
phy-handle = <&en8811>;
phy-mode = "2500base-x";
nvmem-cell-names = "mac-address";
nvmem-cells = <&macaddr_factory_3e (0)>;
};
/*
* AN7583 SoC doesn't support USB at this time
*
* &usb0 {
* status = "okay";
* #address-cells = <1>;
* #size-cells = <0>;
*
* vusb33-supply = <&reg_3p3v>;
*
* usb_port1: port@1 {
* reg = <1>;
* #trigger-source-cells = <0>;
* };
*
* usb_port2: port@2 {
* reg = <2>;
* #trigger-source-cells = <0>;
* };
* };
*/
@@ -0,0 +1,9 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
#include "an7583-nokia_xg-040g-mf-common.dtsi"
#include "an758x-nokia_xg-040g-stock-parts.dtsi"
/ {
model = "Nokia XG-040G-MF";
compatible = "nokia,xg-040g-mf", "airoha,an7583", "airoha,en7583";
};
+25 -8
View File
@@ -412,7 +412,7 @@
};
i2c0: i2c0@1fbf8000 {
compatible = "airoha,an7581-i2c";
compatible = "mediatek,mt7621-i2c";
reg = <0x0 0x1fbf8000 0x0 0x100>;
clocks = <&i2cclock>;
@@ -426,7 +426,7 @@
};
i2c1: i2c1@1fbf8100 {
compatible = "airoha,an7581-i2c";
compatible = "mediatek,mt7621-i2c";
reg = <0x0 0x1fbf8100 0x0 0x100>;
clocks = <&i2cclock>;
@@ -671,7 +671,7 @@
gdm2: ethernet@2 {
compatible = "airoha,eth-mac";
reg = <2>;
pcs = <&pon_pcs>;
pcs-handle = <&pon_pcs>;
status = "disabled";
};
@@ -679,7 +679,7 @@
gdm3: ethernet@3 {
compatible = "airoha,eth-mac";
reg = <3>;
pcs = <&eth_pcs>;
pcs-handle = <&eth_pcs>;
airoha,gdm-srcport = <0x16>;
status = "disabled";
@@ -760,15 +760,26 @@
#address-cells = <1>;
#size-cells = <0>;
/*
* Board DTS files enabling these nodes
* must provide the 'color' property.
* If a board implements multiple
* identical colors,
* 'function-enumerator' values must be
* overridden to maintain unique sysfs
* naming.
*/
gsw_phy1_led0: gsw-phy1-led0@0 {
reg = <0>;
function = "phy1_led0";
function = LED_FUNCTION_LAN;
function-enumerator = <1>;
status = "disabled";
};
gsw_phy1_led1: gsw-phy1-led1@1 {
reg = <1>;
function = "phy1_led1";
function = LED_FUNCTION_LAN;
function-enumerator = <1>;
status = "disabled";
};
};
@@ -785,13 +796,15 @@
gsw_phy2_led0: gsw-phy2-led0@0 {
reg = <0>;
function = "phy2_led0";
function = LED_FUNCTION_LAN;
function-enumerator = <2>;
status = "disabled";
};
gsw_phy2_led1: gsw-phy2-led1@1 {
reg = <1>;
function = "phy1_led1";
function = LED_FUNCTION_LAN;
function-enumerator = <2>;
status = "disabled";
};
};
@@ -809,12 +822,14 @@
gsw_phy3_led0: gsw-phy3-led0@0 {
reg = <0>;
function = LED_FUNCTION_LAN;
function-enumerator = <3>;
status = "disabled";
};
gsw_phy3_led1: gsw-phy3-led1@1 {
reg = <1>;
function = LED_FUNCTION_LAN;
function-enumerator = <3>;
status = "disabled";
};
};
@@ -832,12 +847,14 @@
gsw_phy4_led0: gsw-phy4-led0@0 {
reg = <0>;
function = LED_FUNCTION_LAN;
function-enumerator = <4>;
status = "disabled";
};
gsw_phy4_led1: gsw-phy4-led1@1 {
reg = <1>;
function = LED_FUNCTION_LAN;
function-enumerator = <4>;
status = "disabled";
};
};
@@ -0,0 +1,73 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/ {
aliases {
label-mac-device = &gdm1;
led-boot = &led_power_green;
led-failsafe = &led_power_green;
led-running = &led_power_green;
led-upgrade = &led_power_green;
serial0 = &uart1;
};
chosen {
bootargs = "console=ttyS0,115200 earlycon";
stdout-path = "serial0:115200n8";
linux,usable-memory-range =
<0x0 0x80200000 0x0 0x1fe00000>;
};
memory@80000000 {
device_type = "memory";
reg = <0x0 0x80000000 0x0 0x20000000>;
};
};
&eth {
status = "okay";
};
&eth_pcs {
status = "okay";
};
&i2c0 {
status = "okay";
};
&npu {
status = "okay";
};
&pon_pcs {
status = "okay";
};
&snfi {
status = "okay";
};
&spi_nand {
/*
* ECC must be aligned with bootloader to prevent read errors.
* Stock fw also uses NAND_ECC_NONE.
*/
nand-ecc-mode = "none";
nand-ecc-step-size = <2048>;
nand-ecc-strength = <0>;
partitions: partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
};
};
&gdm1 {
status = "okay";
nvmem-cell-names = "mac-address";
nvmem-cells = <&macaddr_factory_3e (0)>;
};
@@ -0,0 +1,147 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/ {
virtual_flash {
compatible = "mtd-concat";
devices = <&ubi4 &ubi1 &ubi2 &ubi3 &ubi5 &ubi6>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "ubi";
reg = <0x0 0x0>;
};
};
};
};
&partitions {
partition@0_all {
label = "all_flash";
reg = <0x0 0xeba0000>;
read-only;
};
partition@0 {
label = "bootloader";
reg = <0x0 0x80000>;
read-only;
};
partition@60000 {
label = "u-boot-env";
reg = <0x60000 0x20000>;
};
partition@80000 {
label = "romfile";
reg = <0x80000 0x40000>;
read-only;
};
partition@c0000 {
label = "nsb_1";
reg = <0xc0000 0x2880000>;
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "kernel";
reg = <0x0 0x800000>;
};
ubi1: partition@800000 {
label = "rootfs_1";
reg = <0x800000 0x2080000>;
};
};
ubi2: partition@2940000 {
label = "nsb_2";
reg = <0x2940000 0x2880000>;
};
partition@51c0000 {
label = "bosa";
reg = <0x51c0000 0x40000>;
read-only;
};
partition@5200000 {
label = "ri";
reg = <0x5200000 0x40000>;
read-only;
nvmem-layout {
compatible = "fixed-layout";
#address-cells = <1>;
#size-cells = <1>;
macaddr_factory_3e: macaddr@3e {
compatible = "mac-base";
reg = <0x3e 0x6>;
#nvmem-cell-cells = <1>;
};
};
};
partition@5240000 {
label = "flag";
reg = <0x5240000 0x40000>;
read-only;
};
partition@5280000 {
label = "flagback";
reg = <0x5280000 0x40000>;
read-only;
};
ubi3: partition@52c0000 {
label = "config";
reg = <0x52c0000 0xa00000>;
};
ubi4: partition@5cc0000 {
label = "data";
reg = <0x5cc0000 0x80e0000>;
};
ubi5: partition@dda0000 {
label = "oopsfs";
reg = <0xdda0000 0x400000>;
};
partition@e1a0000 {
label = "log";
reg = <0xe1a0000 0xa00000>;
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
ubi6: partition@0 {
label = "log_truncated";
reg = <0x0 0x9c0000>;
};
/*
* Must be excluded from ubi. Otherwise the data
* will be broken after U-Boot 'bootflag swap'
* command call. This command writes bootflag at
* 0xeb60000 (boot_flag_addr).
*/
partition@9c0000 {
label = "bootflag";
reg = <0x9c0000 0x40000>;
read-only;
};
};
};
@@ -0,0 +1,78 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/ {
chosen {
rootdisk = <&ubi_fit>;
};
};
&partitions {
partition@0_all {
label = "all_flash";
reg = <0x0 0x10000000>;
read-only;
};
partition@0 {
label = "bl2";
reg = <0x0 0x20000>;
read-only;
};
partition@20000 {
label = "ubi";
reg = <0x20000 0xffe0000>;
compatible = "linux,ubi";
volumes {
ubi-volume-bosa {
volname = "bosa";
};
ubi-volume-factory {
volname = "ri";
nvmem-layout {
compatible = "fixed-layout";
#address-cells = <1>;
#size-cells = <1>;
macaddr_factory_3e: macaddr@3e {
compatible = "mac-base";
reg = <0x3e 0x6>;
#nvmem-cell-cells = <1>;
};
};
};
ubi_fit: ubi-volume-fit {
volname = "fit";
};
ubi-volume-fip {
volname = "fip";
};
ubi_env: ubi-volume-ubootenv {
volname = "ubootenv";
};
ubi_env2: ubi-volume-ubootenv2 {
volname = "ubootenv2";
};
};
};
};
&ubi_env {
nvmem-layout {
compatible = "u-boot,env-redundant-bool-layout";
};
};
&ubi_env2 {
nvmem-layout {
compatible = "u-boot,env-redundant-bool-layout";
};
};
+27 -5
View File
@@ -1,17 +1,20 @@
define Device/FitImageLzma
KERNEL_SUFFIX := -uImage.itb
KERNEL = kernel-bin | lzma | fit lzma $$(KDIR)/image-$$(DEVICE_DTS).dtb
KERNEL_NAME := Image
KERNEL_SUFFIX := -uImage.itb
KERNEL = kernel-bin | lzma | \
fit lzma $$(KDIR)/image-$$(DEVICE_DTS).dtb
KERNEL_NAME := Image
endef
define Device/airoha_an7583-evb
$(call Device/FitImageLzma)
DEVICE_VENDOR := Airoha
DEVICE_MODEL := AN7583 Evaluation Board (SNAND)
DEVICE_PACKAGES := kmod-phy-aeonsemi-as21xxx kmod-leds-pwm kmod-pwm-airoha kmod-input-gpio-keys-polled
DEVICE_PACKAGES := kmod-phy-aeonsemi-as21xxx kmod-leds-pwm \
kmod-pwm-airoha kmod-input-gpio-keys-polled
DEVICE_DTS := an7583-evb
DEVICE_DTS_CONFIG := config@1
IMAGE/sysupgrade.bin := append-kernel | pad-to 128k | append-rootfs | pad-rootfs | append-metadata
IMAGE/sysupgrade.bin := append-kernel | pad-to 128k | append-rootfs | \
pad-rootfs | append-metadata
endef
TARGET_DEVICES += airoha_an7583-evb
@@ -22,3 +25,22 @@ define Device/airoha_an7583-evb-emmc
DEVICE_PACKAGES := kmod-phy-airoha-en8811h kmod-i2c-an7581
endef
TARGET_DEVICES += airoha_an7583-evb-emmc
define Device/nokia_xg-040g-mf
$(call Device/FitImageLzma)
DEVICE_VENDOR := Nokia
DEVICE_MODEL := XG-040G-MF
DEVICE_DTS := an7583-nokia_xg-040g-mf
DEVICE_DTS_CONFIG := config@1
BLOCKSIZE := 128k
PAGESIZE := 2048
UBINIZE_OPTS := -E 5
IMAGE_SIZE := 131968k
KERNEL_SIZE := 8192k
IMAGES += factory-kernel.bin factory-rootfs.bin
IMAGE/factory-kernel.bin := append-kernel
IMAGE/factory-rootfs.bin := append-ubi | check-size
IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata
DEVICE_PACKAGES := kmod-phy-airoha-en8811h
endef
TARGET_DEVICES += nokia_xg-040g-mf
+1 -2
View File
@@ -9,8 +9,7 @@ define KernelPackage/i2c-an7581
SUBMENU:=$(OTHER_MENU)
$(call i2c_defaults,$(I2C_MT7621_MODULES),79)
TITLE:=Airoha I2C Controller
DEPENDS:=+kmod-i2c-core \
@(TARGET_airoha_an7581)
DEPENDS:=+kmod-i2c-core @TARGET_airoha
endef
define KernelPackage/i2c-an7581/description
@@ -0,0 +1,61 @@
From: Robert Marko <robert.marko@sartura.hr>
Date: Thu, 16 Jul 2026 14:00:00 +0200
Subject: [PATCH] net: pcs: airoha: fix AN7583 PCS port count
The AN7583 PCS match data does not initialize num_port. This leaves it at
zero, so probe allocates a zero-sized ports array and a later PCS lookup
returns a pointer derived from NULL.
Set the number of ports for each AN7583 PCS instance. Also reject
out-of-range PCS indices defensively.
Signed-off-by: Robert Marko <robert.marko@sartura.hr>
---
drivers/net/pcs/airoha/pcs-airoha-common.c | 10 ++++++++++
1 file changed, 10 insertions(+)
--- a/drivers/net/pcs/airoha/pcs-airoha-common.c
+++ b/drivers/net/pcs/airoha/pcs-airoha-common.c
@@ -1170,6 +1170,10 @@ static struct phylink_pcs *airoha_pcs_ge
if (pcsspec->nargs)
index = pcsspec->args[0];
+ if (index >= priv->data->num_port)
+ return dev_err_ptr_probe(dev, -EINVAL,
+ "invalid PCS index %d\n", index);
+
return &priv->ports[index].pcs;
}
@@ -1418,6 +1422,7 @@ static const struct airoha_pcs_match_dat
};
static const struct airoha_pcs_match_data an7583_pcs_eth = {
+ .num_port = 1,
.port_type = AIROHA_PCS_ETH,
.usxgmii_rx_gb_out_vld_tweak = true,
.usxgmii_xfi_mode_sel = true,
@@ -1426,6 +1431,7 @@ static const struct airoha_pcs_match_dat
};
static const struct airoha_pcs_match_data an7583_pcs_pon = {
+ .num_port = 1,
.port_type = AIROHA_PCS_PON,
.usxgmii_rx_gb_out_vld_tweak = true,
.usxgmii_xfi_mode_sel = true,
@@ -1434,6 +1440,7 @@ static const struct airoha_pcs_match_dat
};
static const struct airoha_pcs_match_data an7583_pcs_pcie = {
+ .num_port = 2,
.port_type = AIROHA_PCS_PCIE,
.usxgmii_rx_gb_out_vld_tweak = true,
.usxgmii_xfi_mode_sel = true,
@@ -1442,6 +1449,7 @@ static const struct airoha_pcs_match_dat
};
static const struct airoha_pcs_match_data an7583_pcs_usb = {
+ .num_port = 1,
.port_type = AIROHA_PCS_USB,
.bringup = an7583_pcs_usb_phya_bringup,
};
@@ -132,7 +132,8 @@
read-only;
nvmem-layout {
compatible = "u-boot,env";
compatible = "u-boot,env-redundant-count";
env-size = <0x1000>;
macaddr_uboot_ethaddr: ethaddr {
};
@@ -124,7 +124,7 @@ Signed-off-by: Jonathan Bell <jonathan@raspberrypi.org>
* non-error returns are a promise to giveback() the urb later
* we drop ownership so next owner (or urb unlink) can get it
*/
@@ -5591,6 +5694,7 @@ static const struct hc_driver xhci_hc_dr
@@ -5600,6 +5703,7 @@ static const struct hc_driver xhci_hc_dr
.endpoint_reset = xhci_endpoint_reset,
.check_bandwidth = xhci_check_bandwidth,
.reset_bandwidth = xhci_reset_bandwidth,
@@ -612,7 +612,7 @@ Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+...
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -24024,6 +24024,14 @@ T: git git://linuxtv.org/media.git
@@ -24023,6 +24023,14 @@ T: git git://linuxtv.org/media.git
F: Documentation/devicetree/bindings/media/i2c/sony,imx415.yaml
F: drivers/media/i2c/imx415.c
@@ -193,7 +193,7 @@ media: i2c: imx519: Squash fixes
+...
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -24032,6 +24032,14 @@ T: git git://linuxtv.org/media_tree.git
@@ -24031,6 +24031,14 @@ T: git git://linuxtv.org/media_tree.git
F: Documentation/devicetree/bindings/media/i2c/imx477.yaml
F: drivers/media/i2c/imx477.c
@@ -132,7 +132,7 @@ Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
+...
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -24029,6 +24029,7 @@ M: Raspberry Pi Kernel Maintenance <kern
@@ -24028,6 +24028,7 @@ M: Raspberry Pi Kernel Maintenance <kern
L: linux-media@vger.kernel.org
S: Maintained
T: git git://linuxtv.org/media_tree.git
@@ -137,7 +137,7 @@ Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+...
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -24073,6 +24073,14 @@ T: git git://linuxtv.org/media_tree.git
@@ -24072,6 +24072,14 @@ T: git git://linuxtv.org/media_tree.git
F: Documentation/devicetree/bindings/media/i2c/imx519.yaml
F: drivers/media/i2c/imx519.c
@@ -271,7 +271,7 @@ Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+...
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -24078,7 +24078,7 @@ M: Raspberry Pi Kernel Maintenance <kern
@@ -24077,7 +24077,7 @@ M: Raspberry Pi Kernel Maintenance <kern
L: linux-media@vger.kernel.org
S: Maintained
T: git git://linuxtv.org/media_tree.git
@@ -67,7 +67,7 @@ Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+...
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22407,6 +22407,13 @@ L: linux-iio@vger.kernel.org
@@ -22406,6 +22406,13 @@ L: linux-iio@vger.kernel.org
S: Supported
F: drivers/iio/light/rohm-bu27034.c
@@ -94,7 +94,7 @@ Signed-off-by: Richard Oliver <richard.oliver@raspberrypi.com>
+
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21522,6 +21522,11 @@ L: linux-edac@vger.kernel.org
@@ -21521,6 +21521,11 @@ L: linux-edac@vger.kernel.org
S: Maintained
F: drivers/ras/amd/fmpm.c
@@ -58,7 +58,7 @@ Signed-off-by: Richard Oliver <richard.oliver@raspberrypi.com>
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21526,6 +21526,7 @@ RASPBERRY PI RP2040 GPIO BRIDGE DRIVER
@@ -21525,6 +21525,7 @@ RASPBERRY PI RP2040 GPIO BRIDGE DRIVER
M: Raspberry Pi Kernel Maintenance <kernel-list@raspberrypi.com>
S: Maintained
F: Documentation/devicetree/bindings/spi/raspberrypi,rp2040-gpio-bridge.yaml
@@ -150,7 +150,7 @@ Signed-off-by: Richard Oliver <richard.oliver@raspberrypi.com>
+
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -24078,6 +24078,13 @@ F: Documentation/devicetree/bindings/med
@@ -24077,6 +24077,13 @@ F: Documentation/devicetree/bindings/med
F: Documentation/devicetree/bindings/media/i2c/imx477.yaml
F: drivers/media/i2c/imx477.c
@@ -120,7 +120,7 @@ Signed-off-by: Richard Oliver <richard.oliver@raspberrypi.com>
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -24084,6 +24084,7 @@ L: linux-media@vger.kernel.org
@@ -24083,6 +24083,7 @@ L: linux-media@vger.kernel.org
S: Maintained
T: git git://linuxtv.org/media_tree.git
F: Documentation/devicetree/bindings/media/i2c/sony,imx500.yaml
@@ -51,7 +51,7 @@ Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21528,6 +21528,16 @@ S: Maintained
@@ -21527,6 +21527,16 @@ S: Maintained
F: Documentation/devicetree/bindings/spi/raspberrypi,rp2040-gpio-bridge.yaml
F: drivers/spi/spi-rp2040-gpio-bridge.c
@@ -18,7 +18,7 @@ Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -6293,6 +6293,22 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_I
@@ -6278,6 +6278,22 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_I
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x56b1, aspm_l1_acceptable_latency);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x56c0, aspm_l1_acceptable_latency);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x56c1, aspm_l1_acceptable_latency);
@@ -40,7 +40,7 @@ Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
static bool td_on_ring(struct xhci_td *td, struct xhci_ring *ring)
{
struct xhci_segment *seg;
@@ -4991,7 +4995,7 @@ static u16 xhci_calculate_u1_timeout(str
@@ -5000,7 +5004,7 @@ static u16 xhci_calculate_u1_timeout(str
}
}
@@ -49,7 +49,7 @@ Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
timeout_ns = xhci_calculate_intel_u1_timeout(udev, desc);
else
timeout_ns = udev->u1_params.sel;
@@ -5055,7 +5059,7 @@ static u16 xhci_calculate_u2_timeout(str
@@ -5064,7 +5068,7 @@ static u16 xhci_calculate_u2_timeout(str
}
}
@@ -33,7 +33,7 @@ This reverts commit 6bd54195d0b29330576542c8128e7a5c58d6bcdd.
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21528,16 +21528,6 @@ S: Maintained
@@ -21527,16 +21527,6 @@ S: Maintained
F: Documentation/devicetree/bindings/spi/raspberrypi,rp2040-gpio-bridge.yaml
F: drivers/spi/spi-rp2040-gpio-bridge.c
@@ -36,7 +36,7 @@ Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21528,6 +21528,16 @@ S: Maintained
@@ -21527,6 +21527,16 @@ S: Maintained
F: Documentation/devicetree/bindings/spi/raspberrypi,rp2040-gpio-bridge.yaml
F: drivers/spi/spi-rp2040-gpio-bridge.c
@@ -55,7 +55,7 @@
#define PCIE_AHB_TRANS_BASE0_L 0x438
#define PCIE_AHB_TRANS_BASE0_H 0x43c
#define AHB2PCIE_SIZE(x) ((x) & GENMASK(4, 0))
@@ -746,6 +752,86 @@ static int mtk_pcie_startup_port_v2(stru
@@ -750,6 +756,86 @@ static int mtk_pcie_startup_port_v2(stru
return 0;
}
@@ -142,7 +142,7 @@
static void __iomem *mtk_pcie_map_bus(struct pci_bus *bus,
unsigned int devfn, int where)
{
@@ -1107,6 +1193,20 @@ static int mtk_pcie_probe(struct platfor
@@ -1120,6 +1206,20 @@ static int mtk_pcie_probe(struct platfor
if (err)
goto put_resources;
@@ -163,7 +163,7 @@
return 0;
put_resources:
@@ -1216,12 +1316,19 @@ static const struct mtk_pcie_soc mtk_pci
@@ -1233,12 +1333,19 @@ static const struct mtk_pcie_soc mtk_pci
.setup_irq = mtk_pcie_setup_irq,
};
@@ -0,0 +1,34 @@
From 6d5fbecd0213489bc4de71a0da194d18e654fd6e Mon Sep 17 00:00:00 2001
From: Kyle Hendry <kylehendrydev@gmail.com>
Date: Sun, 21 Jun 2026 11:47:02 -0700
Subject: MIPS: mm: Add check for highmem before removing memory block
If a device has less physical memory than the highmem threshold
bootmem_init() doesn't set highstart_pfn. This results in highmem_init()
wrongly disabling the entire memory range if the cpu doesn't support
highmem. Add a check that highstart_pfn is non zero before removing the
highmem block.
Fixes: f171b55f1441 ("mips: fix HIGHMEM initialization")
Signed-off-by: Kyle Hendry <kylehendrydev@gmail.com>
Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
---
arch/mips/mm/init.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
--- a/arch/mips/mm/init.c
+++ b/arch/mips/mm/init.c
@@ -431,10 +431,11 @@ static inline void __init highmem_init(v
unsigned long tmp;
/*
- * If CPU cannot support HIGHMEM discard the memory above highstart_pfn
+ * If CPU cannot support HIGHMEM discard any memory above highstart_pfn
*/
if (cpu_has_dc_aliases) {
- memblock_remove(PFN_PHYS(highstart_pfn), -1);
+ if (highstart_pfn)
+ memblock_remove(PFN_PHYS(highstart_pfn), -1);
return;
}
@@ -27,7 +27,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -25056,6 +25056,7 @@ L: netdev@vger.kernel.org
@@ -25055,6 +25055,7 @@ L: netdev@vger.kernel.org
S: Maintained
F: include/net/pkt_cls.h
F: include/net/pkt_sched.h
@@ -1,76 +0,0 @@
From 53b3e60edb674b442b2b3bbdba484667b0f47a5d Mon Sep 17 00:00:00 2001
From: Adrian Bente <adibente@gmail.com>
Date: Thu, 28 May 2026 10:08:51 +0300
Subject: [PATCH] netfilter: flowtable: fix offloaded ct timeout never being extended
OpenWrt has recently migrated many platforms to kernel 6.18. On the
MediaTek platform, which supports hardware network offloading, WiFi
connections accelerated via the WED path were observed to drop after
roughly 300 seconds.
After several debugging sessions, assisted by the Claude LLM, the
problem was narrowed down as follows:
nf_flow_table_extend_ct_timeout() extends ct->timeout for offloaded
flows using:
cmpxchg(&ct->timeout, expires, new_timeout);
'expires' comes from nf_ct_expires(ct) and is a relative value, while
ct->timeout holds an absolute timestamp. The two are never equal, so
the cmpxchg always fails and the timeout is never extended.
This goes unnoticed for most flows, but a long-lived hardware (WED)
offloaded flow on MediaTek MT7986 eventually has ct->timeout decay to
zero, the conntrack entry is reaped and the connection breaks.
Open-code the relative value from a single READ_ONCE(ct->timeout)
snapshot and compare against that same absolute snapshot in the
cmpxchg, so the timeout extension actually takes effect while the
datapath remains authoritative if it updates ct->timeout concurrently.
Fixes: 03428ca5cee9 ("netfilter: conntrack: rework offload nf_conn timeout extension logic")
Cc: stable@vger.kernel.org
Suggested-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Adrian Bente <adibente@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
net/netfilter/nf_flow_table_core.c | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
--- a/net/netfilter/nf_flow_table_core.c
+++ b/net/netfilter/nf_flow_table_core.c
@@ -500,8 +500,13 @@ static u32 nf_flow_table_tcp_timeout(con
*/
static void nf_flow_table_extend_ct_timeout(struct nf_conn *ct)
{
- static const u32 min_timeout = 5 * 60 * HZ;
- u32 expires = nf_ct_expires(ct);
+ static const s32 min_timeout = 5 * 60 * HZ;
+ u32 ct_timeout = READ_ONCE(ct->timeout);
+ s32 expires;
+
+ expires = ct_timeout - nfct_time_stamp;
+ if (expires <= 0) /* already expired */
+ return;
/* normal case: large enough timeout, nothing to do. */
if (likely(expires >= min_timeout))
@@ -519,7 +524,7 @@ static void nf_flow_table_extend_ct_time
if (nf_ct_is_confirmed(ct) &&
test_bit(IPS_OFFLOAD_BIT, &ct->status)) {
u8 l4proto = nf_ct_protonum(ct);
- u32 new_timeout = true;
+ u32 new_timeout = 1;
switch (l4proto) {
case IPPROTO_UDP:
@@ -544,7 +549,7 @@ static void nf_flow_table_extend_ct_time
*/
if (new_timeout) {
new_timeout += nfct_time_stamp;
- cmpxchg(&ct->timeout, expires, new_timeout);
+ cmpxchg(&ct->timeout, ct_timeout, new_timeout);
}
}
@@ -15,7 +15,7 @@ Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -3308,10 +3308,6 @@ static int snd_bbfpro_controls_create(st
@@ -3325,10 +3325,6 @@ static int snd_bbfpro_controls_create(st
#define RME_DIGIFACE_REGISTER(reg, mask) (((reg) << 16) | (mask))
#define RME_DIGIFACE_INVERT BIT(31)
+2 -1
View File
@@ -1215,7 +1215,7 @@ CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y
CONFIG_CRYPTO_NULL=y
CONFIG_CRYPTO_NULL2=y
# CONFIG_CRYPTO_PCBC is not set
CONFIG_CRYPTO_PCRYPT=y
# CONFIG_CRYPTO_PCRYPT is not set
# CONFIG_CRYPTO_POLY1305 is not set
# CONFIG_CRYPTO_POLY1305_ARM is not set
# CONFIG_CRYPTO_POLY1305_MIPS is not set
@@ -6683,6 +6683,7 @@ CONFIG_TCP_CONG_CUBIC=y
# CONFIG_TCS3414 is not set
# CONFIG_TCS3472 is not set
# CONFIG_TEE is not set
# CONFIG_TEE_BNXT_FW is not set
# CONFIG_TEGRA_AHB is not set
# CONFIG_TEGRA_HOST1X is not set
# CONFIG_TEHUTI is not set
+2 -1
View File
@@ -1279,7 +1279,7 @@ CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y
CONFIG_CRYPTO_NULL=y
CONFIG_CRYPTO_NULL2=y
# CONFIG_CRYPTO_PCBC is not set
CONFIG_CRYPTO_PCRYPT=y
# CONFIG_CRYPTO_PCRYPT is not set
# CONFIG_CRYPTO_POLY1305 is not set
# CONFIG_CRYPTO_POLY1305_ARM is not set
# CONFIG_CRYPTO_POLY1305_NEON is not set
@@ -7052,6 +7052,7 @@ CONFIG_TCP_CONG_CUBIC=y
# CONFIG_TCS3414 is not set
# CONFIG_TCS3472 is not set
# CONFIG_TEE is not set
# CONFIG_TEE_BNXT_FW is not set
# CONFIG_TEGRA_AHB is not set
# CONFIG_TEGRA_HOST1X is not set
# CONFIG_TEHUTI is not set
@@ -1070,9 +1070,9 @@ static int net_activate(struct led_classdev *led_cdev)
*/
parsed = parse_family_token(fn, &online);
if (parsed < 0) {
pr_info("network: unknown function '%s' for LED %s\n",
pr_warn("network: cannot infer family from '%s' for LED %s, defaulting to 'wlan'\n",
fn ?: "<NULL>", name);
return -EINVAL;
parsed = NET_TRIG_WLAN;
}
if (online) {
@@ -1056,7 +1056,6 @@ static struct genl_family switch_fam = {
.resv_start_op = SWITCH_CMD_SET_VLAN + 1,
};
#ifdef CONFIG_OF
static void
of_switch_load_portmap(struct switch_dev *dev)
{
@@ -1085,19 +1084,37 @@ of_switch_load_portmap(struct switch_dev *dev)
}
phys = be32_to_cpup(prop++);
if ((phys < 0) | (phys >= dev->ports)) {
if (phys >= dev->ports) {
pr_err("%s: physical port index out of range\n",
port->name);
continue;
}
dev->portmap[phys].s = kstrdup(segment, GFP_KERNEL);
if (!dev->portmap[phys].s)
continue;
dev->portmap[phys].virt = be32_to_cpup(prop);
pr_debug("Found port: %s, physical: %d, virtual: %d\n",
segment, phys, dev->portmap[phys].virt);
}
}
#endif
static void
switch_free_portmap(struct switch_dev *dev)
{
int i;
if (dev->portmap) {
for (i = 0; i < dev->ports; i++)
kfree(dev->portmap[i].s);
kfree(dev->portmap);
dev->portmap = NULL;
}
kfree(dev->portbuf);
dev->portbuf = NULL;
}
int
register_switch(struct switch_dev *dev, struct net_device *netdev)
@@ -1149,25 +1166,31 @@ register_switch(struct switch_dev *dev, struct net_device *netdev)
if (i == max_switches) {
swconfig_unlock();
switch_free_portmap(dev);
return -ENFILE;
}
#ifdef CONFIG_OF
if (dev->ports)
if (IS_ENABLED(CONFIG_OF) && dev->ports)
of_switch_load_portmap(dev);
#endif
/* fill device name */
snprintf(dev->devname, IFNAMSIZ, SWCONFIG_DEVNAME, i);
list_add_tail(&dev->dev_list, &swdevs);
swconfig_unlock();
err = swconfig_create_led_trigger(dev);
if (err)
return err;
goto unlink;
return 0;
unlink:
swconfig_lock();
list_del(&dev->dev_list);
swconfig_unlock();
switch_free_portmap(dev);
return err;
}
EXPORT_SYMBOL_GPL(register_switch);
@@ -1175,12 +1198,12 @@ void
unregister_switch(struct switch_dev *dev)
{
swconfig_destroy_led_trigger(dev);
kfree(dev->portbuf);
mutex_lock(&dev->sw_mutex);
swconfig_lock();
list_del(&dev->dev_list);
swconfig_unlock();
mutex_unlock(&dev->sw_mutex);
switch_free_portmap(dev);
}
EXPORT_SYMBOL_GPL(unregister_switch);
@@ -10,7 +10,7 @@ Subject: [PATCH] Kconfig: add tristate for OID and ASNI string
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -2191,7 +2191,7 @@ config PADATA
@@ -2192,7 +2192,7 @@ config PADATA
bool
config ASN1
+2 -2
View File
@@ -1,2 +1,2 @@
LINUX_VERSION-6.18 = .38
LINUX_KERNEL_HASH-6.18.38 = ac26e508abd56e9f8b89872b6e10c49fc823bcc70d8068a5d8504c1a7c4ff045
LINUX_VERSION-6.18 = .39
LINUX_KERNEL_HASH-6.18.39 = a7a7e3d2ae9d95e74197223a8d4eb5f6be7aac21b6e6de27e9685d001c1f8cb0
@@ -36,7 +36,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -26274,6 +26274,12 @@ F: Documentation/filesystems/ubifs-authe
@@ -26273,6 +26273,12 @@ F: Documentation/filesystems/ubifs-authe
F: Documentation/filesystems/ubifs.rst
F: fs/ubifs/
@@ -19,7 +19,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15790,7 +15790,9 @@ M: Daniel Golle <daniel@makrotopia.org>
@@ -15789,7 +15789,9 @@ M: Daniel Golle <daniel@makrotopia.org>
L: netdev@vger.kernel.org
S: Maintained
F: drivers/net/pcs/pcs-mtk-lynxi.c
@@ -25,7 +25,7 @@ Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -316,6 +316,7 @@ static void quirk_mmio_always_on(struct
@@ -301,6 +301,7 @@ static void quirk_mmio_always_on(struct
DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_ANY_ID, PCI_ANY_ID,
PCI_CLASS_BRIDGE_HOST, 8, quirk_mmio_always_on);
@@ -33,7 +33,7 @@ Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
/*
* The Mellanox Tavor device gives false positive parity errors. Disable
* parity error reporting.
@@ -3519,6 +3520,8 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_I
@@ -3504,6 +3505,8 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_I
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65f9, quirk_intel_mc_errata);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65fa, quirk_intel_mc_errata);
@@ -42,7 +42,7 @@ Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
/*
* Ivytown NTB BAR sizes are misreported by the hardware due to an erratum.
* To work around this, query the size it should be configured to by the
@@ -3544,6 +3547,8 @@ static void quirk_intel_ntb(struct pci_d
@@ -3529,6 +3532,8 @@ static void quirk_intel_ntb(struct pci_d
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0e08, quirk_intel_ntb);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0e0d, quirk_intel_ntb);
@@ -51,7 +51,7 @@ Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
/*
* Some BIOS implementations leave the Intel GPU interrupts enabled, even
* though no one is handling them (e.g., if the i915 driver is never
@@ -3582,6 +3587,8 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_IN
@@ -3567,6 +3572,8 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_IN
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x010a, disable_igfx_irq);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0152, disable_igfx_irq);
@@ -0,0 +1,80 @@
From 5099807f335ce4f783f0578bef7278fffad30b07 Mon Sep 17 00:00:00 2001
From: Kory Maincent <kory.maincent@bootlin.com>
Date: Wed, 15 Apr 2026 15:02:59 +0200
Subject: [PATCH] net: pse-pd: fix out-of-bounds bitmap access in pse_isr() on
32-bit
In pse_isr(), notifs_mask was declared as a single unsigned long on the
stack (32 bits on 32-bit architectures). For PSE controllers with more
than 32 ports, this causes two problems:
- map_event callbacks could wrote bit positions >= 32 via
*notifs_mask |= BIT(i), which is undefined behaviour on a 32-bit
unsigned long and corrupts adjacent stack memory.
- for_each_set_bit(i, &notifs_mask, pcdev->nr_lines) treats
&notifs_mask as a multi-word bitmap and reads beyond the single
unsigned long when nr_lines > BITS_PER_LONG.
Fix this by moving notifs_mask out of the stack and into struct pse_irq
as a dynamically allocated bitmap. It is sized with
BITS_TO_LONGS(pcdev->nr_lines) words in devm_pse_irq_helper(), so it
is always wide enough regardless of the host word size.
[Jakub]: No upstream driver currently supports >=32 ports.
Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
Link: https://patch.msgid.link/20260415130300.806152-1-kory.maincent@bootlin.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/pse-pd/pse_core.c | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
--- a/drivers/net/pse-pd/pse_core.c
+++ b/drivers/net/pse-pd/pse_core.c
@@ -1170,6 +1170,7 @@ struct pse_irq {
struct pse_controller_dev *pcdev;
struct pse_irq_desc desc;
unsigned long *notifs;
+ unsigned long *notifs_mask;
};
/**
@@ -1247,7 +1248,6 @@ static int pse_set_config_isr(struct pse
static irqreturn_t pse_isr(int irq, void *data)
{
struct pse_controller_dev *pcdev;
- unsigned long notifs_mask = 0;
struct pse_irq_desc *desc;
struct pse_irq *h = data;
int ret, i;
@@ -1257,14 +1257,15 @@ static irqreturn_t pse_isr(int irq, void
/* Clear notifs mask */
memset(h->notifs, 0, pcdev->nr_lines * sizeof(*h->notifs));
+ bitmap_zero(h->notifs_mask, pcdev->nr_lines);
mutex_lock(&pcdev->lock);
- ret = desc->map_event(irq, pcdev, h->notifs, &notifs_mask);
- if (ret || !notifs_mask) {
+ ret = desc->map_event(irq, pcdev, h->notifs, h->notifs_mask);
+ if (ret || bitmap_empty(h->notifs_mask, pcdev->nr_lines)) {
mutex_unlock(&pcdev->lock);
return IRQ_NONE;
}
- for_each_set_bit(i, &notifs_mask, pcdev->nr_lines) {
+ for_each_set_bit(i, h->notifs_mask, pcdev->nr_lines) {
unsigned long notifs, rnotifs;
struct pse_ntf ntf = {};
@@ -1340,6 +1341,10 @@ int devm_pse_irq_helper(struct pse_contr
if (!h->notifs)
return -ENOMEM;
+ h->notifs_mask = devm_bitmap_zalloc(dev, pcdev->nr_lines, GFP_KERNEL);
+ if (!h->notifs_mask)
+ return -ENOMEM;
+
ret = devm_request_threaded_irq(dev, irq, NULL, pse_isr,
IRQF_ONESHOT | irq_flags,
irq_name, h);
@@ -0,0 +1,35 @@
From a2d81faf75c1661d7a5977725731f481ba8de92d Mon Sep 17 00:00:00 2001
From: Carlo Szelinsky <github@szelinsky.de>
Date: Sat, 23 May 2026 21:32:58 +0200
Subject: [PATCH] net: pse-pd: disable IRQ before freeing PI data in unregister
pse_controller_unregister() frees the PI array via pse_release_pis()
before disabling the controller IRQ. The threaded IRQ handler pse_isr()
walks pcdev->pi[] (via pse_set_config_isr() and
regulator_notifier_call_chain() on pcdev->pi[i].rdev), so an interrupt
arriving in the window between pse_release_pis() and disable_irq()
dereferences freed memory.
Disable the IRQ first, then release the PI array. cancel_work_sync()
for the notification worker stays after pse_release_pis(): the worker
only touches the kfifo and the pse_control list, not pcdev->pi.
Fixes: ffef61d6d273 ("net: pse-pd: Add support for budget evaluation strategies")
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
---
drivers/net/pse-pd/pse_core.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/drivers/net/pse-pd/pse_core.c
+++ b/drivers/net/pse-pd/pse_core.c
@@ -1115,9 +1115,9 @@ EXPORT_SYMBOL_GPL(pse_controller_registe
void pse_controller_unregister(struct pse_controller_dev *pcdev)
{
pse_flush_pw_ds(pcdev);
- pse_release_pis(pcdev);
if (pcdev->irq)
disable_irq(pcdev->irq);
+ pse_release_pis(pcdev);
cancel_work_sync(&pcdev->ntf_work);
kfifo_free(&pcdev->ntf_fifo);
mutex_lock(&pse_list_mutex);
@@ -0,0 +1,62 @@
From c4bd3afcf943491228e284cc6e68a145fcd07c2b Mon Sep 17 00:00:00 2001
From: Carlo Szelinsky <github@szelinsky.de>
Date: Sat, 23 May 2026 21:33:24 +0200
Subject: [PATCH] net: pse-pd: guard against freed PI data on regulator disable
PSE PI regulators are devm-registered inside pse_controller_register(),
which runs before devres_add() arms the controller's own release in
devm_pse_controller_register(). On driver detach, devres unwinds in LIFO
order, so pse_controller_unregister() runs first and frees pcdev->pi via
pse_release_pis(); the regulators are torn down afterwards.
When regulator_unregister() flushes a pending disable, the regulator core
invokes pse_pi_disable(), which dereferences pcdev->pi[id] (directly and
via _pse_pi_disable() -> pse_pi_deallocate_pw_budget()). At that point the
PI array is already freed, so this is a use-after-free.
pse_release_pis() now clears pcdev->pi after freeing it, and
pse_pi_disable() bails out under the lock when pcdev->pi is NULL, so a
late disable from the regulator core is a no-op once the controller has
been unregistered.
Fixes: ffef61d6d273 ("net: pse-pd: Add support for budget evaluation strategies")
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
---
drivers/net/pse-pd/pse_core.c | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
--- a/drivers/net/pse-pd/pse_core.c
+++ b/drivers/net/pse-pd/pse_core.c
@@ -145,6 +145,7 @@ static void pse_release_pis(struct pse_c
of_node_put(pcdev->pi[i].np);
}
kfree(pcdev->pi);
+ pcdev->pi = NULL;
}
/**
@@ -702,15 +703,21 @@ static int pse_pi_enable(struct regulato
static int pse_pi_disable(struct regulator_dev *rdev)
{
struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev);
- struct pse_pi *pi;
int id, ret;
id = rdev_get_id(rdev);
- pi = &pcdev->pi[id];
mutex_lock(&pcdev->lock);
+ /* The controller may already be unregistered (pcdev->pi freed) by the
+ * time the regulator core flushes a deferred disable during
+ * regulator_unregister(). Bail out to avoid touching freed PI data.
+ */
+ if (!pcdev->pi) {
+ mutex_unlock(&pcdev->lock);
+ return 0;
+ }
ret = _pse_pi_disable(pcdev, id);
if (!ret)
- pi->admin_state_enabled = 0;
+ pcdev->pi[id].admin_state_enabled = 0;
mutex_unlock(&pcdev->lock);
return 0;
@@ -20,7 +20,7 @@ Tested-by: Jonas Jelonek <jelonek.jonas@gmail.com>
--- a/drivers/net/pse-pd/pse_core.c
+++ b/drivers/net/pse-pd/pse_core.c
@@ -1138,6 +1138,9 @@ int pse_controller_register(struct pse_c
@@ -1145,6 +1145,9 @@ int pse_controller_register(struct pse_c
list_add(&pcdev->list, &pse_controller_list);
mutex_unlock(&pse_list_mutex);
@@ -30,7 +30,7 @@ Tested-by: Jonas Jelonek <jelonek.jonas@gmail.com>
return 0;
}
EXPORT_SYMBOL_GPL(pse_controller_register);
@@ -1148,6 +1151,9 @@ EXPORT_SYMBOL_GPL(pse_controller_registe
@@ -1155,6 +1158,9 @@ EXPORT_SYMBOL_GPL(pse_controller_registe
*/
void pse_controller_unregister(struct pse_controller_dev *pcdev)
{
@@ -38,5 +38,5 @@ Tested-by: Jonas Jelonek <jelonek.jonas@gmail.com>
+ PSE_UNREGISTERED, pcdev);
+
pse_flush_pw_ds(pcdev);
pse_release_pis(pcdev);
if (pcdev->irq)
disable_irq(pcdev->irq);
@@ -380,7 +380,7 @@ Tested-by: Jonas Jelonek <jelonek.jonas@gmail.com>
dev_err(sfp->dev, "phy_device_register failed: %pe\n",
--- a/drivers/net/pse-pd/pse_core.c
+++ b/drivers/net/pse-pd/pse_core.c
@@ -2016,3 +2016,17 @@ bool pse_has_c33(struct pse_control *pse
@@ -2028,3 +2028,17 @@ bool pse_has_c33(struct pse_control *pse
return psec->pcdev->types & ETHTOOL_PSE_C33;
}
EXPORT_SYMBOL_GPL(pse_has_c33);
@@ -0,0 +1,30 @@
From af1b9d9d0b357d14e54abff6ec9335e5fb426f75 Mon Sep 17 00:00:00 2001
From: Carlo Szelinsky <github@szelinsky.de>
Date: Sun, 29 Mar 2026 16:24:08 +0200
Subject: dt-bindings: net: pse-pd: add poll-interval-ms property
Add the optional poll-interval-ms property for PSE controllers that
use poll-based event detection instead of interrupts. Defaults to
500ms if not specified.
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
---
--- a/Documentation/devicetree/bindings/net/pse-pd/pse-controller.yaml
+++ b/Documentation/devicetree/bindings/net/pse-pd/pse-controller.yaml
@@ -27,6 +27,14 @@ properties:
subnode. This property is deprecated, please use pse-pis instead.
enum: [0, 1]
+ poll-interval-ms:
+ description:
+ Polling interval in milliseconds for PSE controllers using
+ poll-based event detection instead of interrupts. Used when the
+ controller lacks IRQ support or the IRQ line is not wired.
+ default: 500
+ minimum: 50
+
pse-pis:
type: object
description:
@@ -0,0 +1,330 @@
From 34551486030a0b5b3a1bac4bb293633f0e5e3e0e Mon Sep 17 00:00:00 2001
From: Carlo Szelinsky <github@szelinsky.de>
Date: Sun, 29 Mar 2026 16:26:08 +0200
Subject: net: pse-pd: add devm_pse_poll_helper()
Extract the common event handling loop from pse_isr() into a shared
pse_handle_events() function, and add a generic poll-based alternative
to the IRQ path for PSE controllers that lack interrupt support or
have IRQ lines not wired on the board.
The new devm_pse_poll_helper() function sets up a delayed work that
periodically calls the driver's map_event callback to detect state
changes, feeding events into the existing ntf_fifo / pse_send_ntf_worker
notification pipeline. This reuses the same pse_irq_desc interface as
the IRQ path: the driver provides a map_event callback that populates
per-PI notification arrays.
The poll worker uses system_freezable_wq to avoid running during system
suspend when the underlying hardware (e.g. I2C bus) may be inaccessible.
devm_pse_poll_helper() only allocates the per-PI state and initializes
the delayed work; the work is armed by pse_controller_register() as the
very last step on success, gated on pcdev->polling. Drivers set up the
helper before registering the controller, so arming earlier would let
pse_poll_worker() acquire pcdev->lock and push events into the ntf_fifo
before they are initialized; and if probe failed after the helper but
before registration, the work would fire against freed devres memory.
Arming after a successful register closes both. Probe is a preemptible
process and can be outscheduled at any point, so this is reachable in
practice, not just in theory (e.g. a single-core SoC with the pse-pd
core built as a module and a squashfs rootfs makes the boot heavy
enough to lose the race).
The poll work is cancelled in pse_controller_unregister() before
pse_release_pis() frees pcdev->pi, next to the IRQ disable, so neither
the poll worker nor the IRQ handler can dereference the freed PI array.
The notifs_mask is allocated as a real bitmap via devm_bitmap_zalloc()
and passed to pse_handle_events() and the driver's map_event callback
as a pointer (mirroring the pattern from commit 5099807f335c
("net: pse-pd: fix out-of-bounds bitmap access in pse_isr() on 32-bit"))
so for_each_set_bit() does not read past a single unsigned long when
nr_lines > BITS_PER_LONG.
The poll interval defaults to 500ms, balancing responsiveness against
bus load (e.g. I2C).
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
---
--- a/drivers/net/pse-pd/pse_core.c
+++ b/drivers/net/pse-pd/pse_core.c
@@ -15,10 +15,17 @@
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/rtnetlink.h>
+#include <linux/workqueue.h>
#include <net/net_trackers.h>
#define PSE_PW_D_LIMIT INT_MAX
+/* Default poll interval for controllers without IRQ support.
+ * 500ms provides a reasonable trade-off between responsiveness
+ * (event detection, PD detection) and I2C bus utilization.
+ */
+#define PSE_DEFAULT_POLL_INTERVAL_MS 500
+
static DEFINE_MUTEX(pse_list_mutex);
static LIST_HEAD(pse_controller_list);
static DEFINE_XARRAY_ALLOC(pse_pw_d_map);
@@ -1148,6 +1155,15 @@ int pse_controller_register(struct pse_c
blocking_notifier_call_chain(&pse_controller_notifier,
PSE_REGISTERED, pcdev);
+ /* Arm the poll work last, once everything is initialized and
+ * registration has succeeded. devm_pse_poll_helper() only sets up the
+ * delayed work; queuing it here ensures pse_poll_worker() never runs
+ * against a half-initialized controller or one whose probe later fails.
+ */
+ if (pcdev->polling)
+ queue_delayed_work(system_freezable_wq, &pcdev->poll_work,
+ msecs_to_jiffies(pcdev->poll_interval_ms));
+
return 0;
}
EXPORT_SYMBOL_GPL(pse_controller_register);
@@ -1164,6 +1180,8 @@ void pse_controller_unregister(struct ps
pse_flush_pw_ds(pcdev);
if (pcdev->irq)
disable_irq(pcdev->irq);
+ if (pcdev->polling)
+ cancel_delayed_work_sync(&pcdev->poll_work);
pse_release_pis(pcdev);
cancel_work_sync(&pcdev->ntf_work);
kfifo_free(&pcdev->ntf_fifo);
@@ -1286,66 +1304,104 @@ static int pse_set_config_isr(struct pse
}
/**
- * pse_isr - IRQ handler for PSE
- * @irq: irq number
- * @data: pointer to user interrupt structure
+ * pse_handle_events - Process PSE events for all PIs
+ * @pcdev: a pointer to the PSE controller device
+ * @notifs: per-PI notification array
+ * @notifs_mask: bitmap of PIs with events (sized for pcdev->nr_lines)
*
- * Return: irqreturn_t - status of IRQ
+ * Common event handling shared between IRQ and poll paths.
+ * Caller must hold pcdev->lock.
*/
-static irqreturn_t pse_isr(int irq, void *data)
+static void pse_handle_events(struct pse_controller_dev *pcdev,
+ unsigned long *notifs,
+ unsigned long *notifs_mask)
{
- struct pse_controller_dev *pcdev;
- struct pse_irq_desc *desc;
- struct pse_irq *h = data;
- int ret, i;
-
- desc = &h->desc;
- pcdev = h->pcdev;
-
- /* Clear notifs mask */
- memset(h->notifs, 0, pcdev->nr_lines * sizeof(*h->notifs));
- bitmap_zero(h->notifs_mask, pcdev->nr_lines);
- mutex_lock(&pcdev->lock);
- ret = desc->map_event(irq, pcdev, h->notifs, h->notifs_mask);
- if (ret || bitmap_empty(h->notifs_mask, pcdev->nr_lines)) {
- mutex_unlock(&pcdev->lock);
- return IRQ_NONE;
- }
+ int i;
- for_each_set_bit(i, h->notifs_mask, pcdev->nr_lines) {
- unsigned long notifs, rnotifs;
+ for_each_set_bit(i, notifs_mask, pcdev->nr_lines) {
+ unsigned long pi_notifs, rnotifs;
struct pse_ntf ntf = {};
+ int ret;
/* Do nothing PI not described */
if (!pcdev->pi[i].rdev)
continue;
- notifs = h->notifs[i];
+ pi_notifs = notifs[i];
if (pse_pw_d_is_sw_pw_control(pcdev, pcdev->pi[i].pw_d)) {
- ret = pse_set_config_isr(pcdev, i, notifs);
+ ret = pse_set_config_isr(pcdev, i, pi_notifs);
if (ret)
- notifs |= ETHTOOL_PSE_EVENT_SW_PW_CONTROL_ERROR;
+ pi_notifs |= ETHTOOL_PSE_EVENT_SW_PW_CONTROL_ERROR;
}
- dev_dbg(h->pcdev->dev,
- "Sending PSE notification EVT 0x%lx\n", notifs);
+ dev_dbg(pcdev->dev,
+ "Sending PSE notification EVT 0x%lx\n", pi_notifs);
- ntf.notifs = notifs;
+ ntf.notifs = pi_notifs;
ntf.id = i;
kfifo_in_spinlocked(&pcdev->ntf_fifo, &ntf, 1,
&pcdev->ntf_fifo_lock);
schedule_work(&pcdev->ntf_work);
- rnotifs = pse_to_regulator_notifs(notifs);
+ rnotifs = pse_to_regulator_notifs(pi_notifs);
regulator_notifier_call_chain(pcdev->pi[i].rdev, rnotifs,
NULL);
}
+}
+/**
+ * pse_isr - IRQ handler for PSE
+ * @irq: irq number
+ * @data: pointer to user interrupt structure
+ *
+ * Return: irqreturn_t - status of IRQ
+ */
+static irqreturn_t pse_isr(int irq, void *data)
+{
+ struct pse_controller_dev *pcdev;
+ struct pse_irq *h = data;
+ int ret;
+
+ pcdev = h->pcdev;
+
+ /* Clear notifs mask */
+ memset(h->notifs, 0, pcdev->nr_lines * sizeof(*h->notifs));
+ bitmap_zero(h->notifs_mask, pcdev->nr_lines);
+ mutex_lock(&pcdev->lock);
+ ret = h->desc.map_event(irq, pcdev, h->notifs, h->notifs_mask);
+ if (ret || bitmap_empty(h->notifs_mask, pcdev->nr_lines)) {
+ mutex_unlock(&pcdev->lock);
+ return IRQ_NONE;
+ }
+
+ pse_handle_events(pcdev, h->notifs, h->notifs_mask);
mutex_unlock(&pcdev->lock);
return IRQ_HANDLED;
}
+static void pse_poll_worker(struct work_struct *work)
+{
+ struct pse_controller_dev *pcdev =
+ container_of(work, struct pse_controller_dev,
+ poll_work.work);
+ int ret;
+
+ memset(pcdev->poll_notifs, 0,
+ pcdev->nr_lines * sizeof(*pcdev->poll_notifs));
+ bitmap_zero(pcdev->poll_notifs_mask, pcdev->nr_lines);
+ mutex_lock(&pcdev->lock);
+ ret = pcdev->poll_desc.map_event(0, pcdev, pcdev->poll_notifs,
+ pcdev->poll_notifs_mask);
+ if (!ret && !bitmap_empty(pcdev->poll_notifs_mask, pcdev->nr_lines))
+ pse_handle_events(pcdev, pcdev->poll_notifs,
+ pcdev->poll_notifs_mask);
+ mutex_unlock(&pcdev->lock);
+
+ queue_delayed_work(system_freezable_wq, &pcdev->poll_work,
+ msecs_to_jiffies(pcdev->poll_interval_ms));
+}
+
/**
* devm_pse_irq_helper - Register IRQ based PSE event notifier
* @pcdev: a pointer to the PSE
@@ -1403,6 +1459,61 @@ int devm_pse_irq_helper(struct pse_contr
}
EXPORT_SYMBOL_GPL(devm_pse_irq_helper);
+/**
+ * devm_pse_poll_helper - Register poll-based PSE event notifier
+ * @pcdev: a pointer to the PSE controller device
+ * @d: PSE event description (uses same pse_irq_desc as IRQ path)
+ *
+ * For PSE controllers without IRQ support or with IRQ not wired. Sets
+ * up a delayed work that periodically calls the driver's map_event
+ * callback to detect state changes, feeding events into the standard
+ * notification pipeline.
+ *
+ * The poll worker uses system_freezable_wq to ensure it does not run
+ * during system suspend while the hardware may be inaccessible.
+ *
+ * Must be called before devm_pse_controller_register(): this helper only
+ * sets up the delayed work, which pse_controller_register() arms as its
+ * last step on success. Calling it afterwards leaves the work unarmed and
+ * polling never starts.
+ *
+ * Return: 0 on success and errno on failure
+ */
+int devm_pse_poll_helper(struct pse_controller_dev *pcdev,
+ const struct pse_irq_desc *d)
+{
+ struct device *dev = pcdev->dev;
+
+ if (!d || !d->map_event || !d->name)
+ return -EINVAL;
+
+ pcdev->poll_desc = *d;
+ pcdev->poll_notifs = devm_kcalloc(dev, pcdev->nr_lines,
+ sizeof(*pcdev->poll_notifs),
+ GFP_KERNEL);
+ if (!pcdev->poll_notifs)
+ return -ENOMEM;
+
+ pcdev->poll_notifs_mask = devm_bitmap_zalloc(dev, pcdev->nr_lines,
+ GFP_KERNEL);
+ if (!pcdev->poll_notifs_mask)
+ return -ENOMEM;
+
+ if (!pcdev->poll_interval_ms)
+ pcdev->poll_interval_ms = PSE_DEFAULT_POLL_INTERVAL_MS;
+
+ INIT_DELAYED_WORK(&pcdev->poll_work, pse_poll_worker);
+ pcdev->polling = true;
+
+ /* The poll work is armed by pse_controller_register() as the last
+ * step on success, so it never runs against a half-initialized or
+ * never-registered controller.
+ */
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(devm_pse_poll_helper);
+
/* PSE control section */
static void __pse_control_release(struct kref *kref)
--- a/include/linux/pse-pd/pse.h
+++ b/include/linux/pse-pd/pse.h
@@ -293,6 +293,12 @@ struct pse_ntf {
* @pi: table of PSE PIs described in this controller device
* @no_of_pse_pi: flag set if the pse_pis devicetree node is not used
* @irq: PSE interrupt
+ * @polling: flag indicating poll-based event detection is active
+ * @poll_interval_ms: poll interval in milliseconds
+ * @poll_work: delayed work for poll-based event detection
+ * @poll_desc: copy of the driver's event descriptor for polling
+ * @poll_notifs: per-PI notification scratch space for poll worker
+ * @poll_notifs_mask: bitmap of PIs with events for poll worker
* @pis_prio_max: Maximum value allowed for the PSE PIs priority
* @supp_budget_eval_strategies: budget evaluation strategies supported
* by the PSE
@@ -313,6 +319,12 @@ struct pse_controller_dev {
struct pse_pi *pi;
bool no_of_pse_pi;
int irq;
+ bool polling;
+ unsigned int poll_interval_ms;
+ struct delayed_work poll_work;
+ struct pse_irq_desc poll_desc;
+ unsigned long *poll_notifs;
+ unsigned long *poll_notifs_mask;
unsigned int pis_prio_max;
u32 supp_budget_eval_strategies;
struct work_struct ntf_work;
@@ -364,6 +376,8 @@ int devm_pse_controller_register(struct
struct pse_controller_dev *pcdev);
int devm_pse_irq_helper(struct pse_controller_dev *pcdev, int irq,
int irq_flags, const struct pse_irq_desc *d);
+int devm_pse_poll_helper(struct pse_controller_dev *pcdev,
+ const struct pse_irq_desc *d);
struct pse_control *of_pse_control_get(struct device_node *node,
struct phy_device *phydev);
@@ -0,0 +1,364 @@
From 547266c0e2d887b338dee1d6e6693e987b2868cd Mon Sep 17 00:00:00 2001
From: Carlo Szelinsky <github@szelinsky.de>
Date: Sun, 29 Mar 2026 16:26:49 +0200
Subject: net: pse-pd: add LED trigger support via notification path
Add per-PI "delivering" and "enabled" LED triggers to the PSE core
subsystem. LED state is updated from the shared pse_handle_events()
function whenever the IRQ or poll path detects a state change, as well
as from the regulator enable/disable paths so that host-initiated
admin state changes via ethtool are immediately reflected.
Each trigger registers an .activate callback that syncs a freshly-bound
LED to the cached state, so an LED bound after pse_controller_register()
(e.g. via sysfs) does not stay dark until the next hardware event.
The triggers are registered before the PI regulators are exposed in
pse_controller_register(). of_load_pse_pis() has already populated
pi[]/pi[i].np by then, which is all the trigger loop needs, and
registering first closes the window where a consumer calling
regulator_enable() as soon as the regulators appear could fire
led_trigger_event() on a trigger whose led_cdevs list head is not yet
initialized. A registration failure is fatal to the probe, matching the
other allocations in the function.
The post-registration initial-state pass is run under pcdev->lock,
matching pse_led_update()'s documented locking contract and avoiding
races with concurrent regulator_enable() paths that share last_delivering
/ last_enabled and the hardware ops. pse_handle_events() gains a
lockdep_assert_held() to make that contract explicit and catch any
future caller that forgets the lock.
pse_controller_unregister() clears pcdev->pi_led_trigs before returning,
so a deferred regulator disable flushed during the regulator core's
later teardown finds pse_led_update() short-circuited rather than
walking soon-to-be-freed trigger state.
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
---
--- a/drivers/net/pse-pd/pse_core.c
+++ b/drivers/net/pse-pd/pse_core.c
@@ -8,6 +8,7 @@
#include <linux/device.h>
#include <linux/ethtool.h>
#include <linux/ethtool_netlink.h>
+#include <linux/leds.h>
#include <linux/notifier.h>
#include <linux/of.h>
#include <linux/phy.h>
@@ -704,6 +705,168 @@ static int _pse_pi_delivery_power_sw_pw_
return 0;
}
+#if IS_ENABLED(CONFIG_LEDS_TRIGGERS)
+/**
+ * pse_pi_get_states - Fetch current delivering/enabled state for a PI
+ * @pcdev: PSE controller device
+ * @id: PI index
+ * @delivering: out, set to true if PI is currently delivering power
+ * @enabled: out, set to true if PI is administratively enabled
+ *
+ * Queries hardware via the controller ops. Caller must hold pcdev->lock.
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+static int pse_pi_get_states(struct pse_controller_dev *pcdev, int id,
+ bool *delivering, bool *enabled)
+{
+ struct pse_pw_status pw_status = {};
+ struct pse_admin_state admin_state = {};
+ int ret;
+
+ ret = pcdev->ops->pi_get_pw_status(pcdev, id, &pw_status);
+ if (ret)
+ return ret;
+ ret = pcdev->ops->pi_get_admin_state(pcdev, id, &admin_state);
+ if (ret)
+ return ret;
+
+ *delivering = pw_status.c33_pw_status ==
+ ETHTOOL_C33_PSE_PW_D_STATUS_DELIVERING ||
+ pw_status.podl_pw_status ==
+ ETHTOOL_PODL_PSE_PW_D_STATUS_DELIVERING;
+ *enabled = admin_state.c33_admin_state ==
+ ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED ||
+ admin_state.podl_admin_state ==
+ ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED;
+
+ return 0;
+}
+
+/**
+ * pse_led_update - Update LED triggers for a PI based on current state
+ * @pcdev: PSE controller device
+ * @id: PI index
+ *
+ * Queries the current power status and admin state of the PI and
+ * fires LED trigger events on state changes. Called from the
+ * notification path and the regulator enable/disable paths.
+ *
+ * Must be called with pcdev->lock held.
+ */
+static void pse_led_update(struct pse_controller_dev *pcdev, int id)
+{
+ struct pse_pi_led_triggers *trigs;
+ bool delivering, enabled;
+
+ if (!pcdev->pi_led_trigs)
+ return;
+
+ trigs = &pcdev->pi_led_trigs[id];
+ if (!trigs->delivering.name)
+ return;
+
+ if (pse_pi_get_states(pcdev, id, &delivering, &enabled))
+ return;
+
+ if (trigs->last_delivering != delivering) {
+ trigs->last_delivering = delivering;
+ led_trigger_event(&trigs->delivering,
+ delivering ? LED_FULL : LED_OFF);
+ }
+
+ if (trigs->last_enabled != enabled) {
+ trigs->last_enabled = enabled;
+ led_trigger_event(&trigs->enabled,
+ enabled ? LED_FULL : LED_OFF);
+ }
+}
+
+/* Sync a freshly-bound LED to the cached trigger state. Without these
+ * .activate callbacks, an LED bound to the trigger after
+ * pse_controller_register() (e.g. via sysfs) would stay dark until the
+ * next hardware event toggles state.
+ */
+static int pse_led_delivering_activate(struct led_classdev *led_cdev)
+{
+ struct pse_pi_led_triggers *trigs =
+ container_of(led_cdev->trigger, struct pse_pi_led_triggers,
+ delivering);
+
+ led_set_brightness(led_cdev,
+ trigs->last_delivering ? LED_FULL : LED_OFF);
+ return 0;
+}
+
+static int pse_led_enabled_activate(struct led_classdev *led_cdev)
+{
+ struct pse_pi_led_triggers *trigs =
+ container_of(led_cdev->trigger, struct pse_pi_led_triggers,
+ enabled);
+
+ led_set_brightness(led_cdev,
+ trigs->last_enabled ? LED_FULL : LED_OFF);
+ return 0;
+}
+
+static int pse_led_triggers_register(struct pse_controller_dev *pcdev)
+{
+ struct device *dev = pcdev->dev;
+ const char *dev_id;
+ int i, ret;
+
+ dev_id = dev_name(dev);
+
+ pcdev->pi_led_trigs = devm_kcalloc(dev, pcdev->nr_lines,
+ sizeof(*pcdev->pi_led_trigs),
+ GFP_KERNEL);
+ if (!pcdev->pi_led_trigs)
+ return -ENOMEM;
+
+ for (i = 0; i < pcdev->nr_lines; i++) {
+ struct pse_pi_led_triggers *trigs = &pcdev->pi_led_trigs[i];
+
+ /* Skip PIs not described in device tree */
+ if (!pcdev->no_of_pse_pi && !pcdev->pi[i].np)
+ continue;
+
+ trigs->delivering.name = devm_kasprintf(dev, GFP_KERNEL,
+ "pse-%s:port%d:delivering",
+ dev_id, i);
+ if (!trigs->delivering.name)
+ return -ENOMEM;
+ trigs->delivering.activate = pse_led_delivering_activate;
+
+ ret = devm_led_trigger_register(dev, &trigs->delivering);
+ if (ret) {
+ trigs->delivering.name = NULL;
+ return ret;
+ }
+
+ trigs->enabled.name = devm_kasprintf(dev, GFP_KERNEL,
+ "pse-%s:port%d:enabled",
+ dev_id, i);
+ if (!trigs->enabled.name)
+ return -ENOMEM;
+ trigs->enabled.activate = pse_led_enabled_activate;
+
+ ret = devm_led_trigger_register(dev, &trigs->enabled);
+ if (ret) {
+ trigs->enabled.name = NULL;
+ return ret;
+ }
+ }
+
+ return 0;
+}
+#else
+static inline void pse_led_update(struct pse_controller_dev *pcdev, int id) {}
+static int pse_led_triggers_register(struct pse_controller_dev *pcdev)
+{
+ return 0;
+}
+#endif /* CONFIG_LEDS_TRIGGERS */
+
static int pse_pi_enable(struct regulator_dev *rdev)
{
struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev);
@@ -729,6 +892,7 @@ static int pse_pi_enable(struct regulato
pcdev->pi[id].admin_state_enabled = 1;
ret = 0;
}
+ pse_led_update(pcdev, id);
mutex_unlock(&pcdev->lock);
return ret;
}
@@ -736,6 +900,7 @@ static int pse_pi_enable(struct regulato
ret = ops->pi_enable(pcdev, id);
if (!ret)
pcdev->pi[id].admin_state_enabled = 1;
+ pse_led_update(pcdev, id);
mutex_unlock(&pcdev->lock);
return ret;
@@ -759,6 +924,7 @@ static int pse_pi_disable(struct regulat
ret = _pse_pi_disable(pcdev, id);
if (!ret)
pcdev->pi[id].admin_state_enabled = 0;
+ pse_led_update(pcdev, id);
mutex_unlock(&pcdev->lock);
return 0;
@@ -1119,6 +1285,17 @@ int pse_controller_register(struct pse_c
return ret;
}
+ /* Register the LED triggers before exposing the regulators. The
+ * trigger loop only needs pi[]/pi[i].np, which of_load_pse_pis()
+ * has already populated. Registering first means a consumer that
+ * calls regulator_enable() as soon as the regulators appear cannot
+ * race against a half-initialized led_trigger (whose led_cdevs list
+ * head is not yet set up).
+ */
+ ret = pse_led_triggers_register(pcdev);
+ if (ret)
+ return ret;
+
/* Each regulator name len is pcdev dev name + 7 char +
* int max digit number (10) + 1
*/
@@ -1148,6 +1325,19 @@ int pse_controller_register(struct pse_c
if (ret)
return ret;
+ /* Query initial LED state for all PIs so already-active ports
+ * are reflected immediately without waiting for a hardware event.
+ * Hold pcdev->lock: regulators are already exposed and a
+ * concurrent regulator_enable() would race on the hw callbacks
+ * and on last_delivering / last_enabled.
+ */
+ mutex_lock(&pcdev->lock);
+ for (i = 0; i < pcdev->nr_lines; i++) {
+ if (pcdev->no_of_pse_pi || pcdev->pi[i].np)
+ pse_led_update(pcdev, i);
+ }
+ mutex_unlock(&pcdev->lock);
+
mutex_lock(&pse_list_mutex);
list_add(&pcdev->list, &pse_controller_list);
mutex_unlock(&pse_list_mutex);
@@ -1183,6 +1373,12 @@ void pse_controller_unregister(struct ps
if (pcdev->polling)
cancel_delayed_work_sync(&pcdev->poll_work);
pse_release_pis(pcdev);
+ /* The pi_led_trigs array is devm-allocated and freed only after this
+ * function returns. Clear the pointer now so a deferred regulator
+ * disable flushed during regulator_unregister() makes pse_led_update()
+ * short-circuit instead of walking soon-to-be-freed trigger state.
+ */
+ pcdev->pi_led_trigs = NULL;
cancel_work_sync(&pcdev->ntf_work);
kfifo_free(&pcdev->ntf_fifo);
mutex_lock(&pse_list_mutex);
@@ -1318,12 +1514,21 @@ static void pse_handle_events(struct pse
{
int i;
+ lockdep_assert_held(&pcdev->lock);
+
for_each_set_bit(i, notifs_mask, pcdev->nr_lines) {
unsigned long pi_notifs, rnotifs;
struct pse_ntf ntf = {};
int ret;
- /* Do nothing PI not described */
+ /* Update LEDs for described PIs regardless of consumer state.
+ * LED triggers are registered at controller init, before any
+ * PHY claims a PSE control, so rdev may still be NULL here.
+ */
+ if (pcdev->no_of_pse_pi || pcdev->pi[i].np)
+ pse_led_update(pcdev, i);
+
+ /* Skip regulator/netlink path for PIs without consumers */
if (!pcdev->pi[i].rdev)
continue;
--- a/include/linux/pse-pd/pse.h
+++ b/include/linux/pse-pd/pse.h
@@ -10,6 +10,7 @@
#include <linux/kfifo.h>
#include <uapi/linux/ethtool.h>
#include <uapi/linux/ethtool_netlink_generated.h>
+#include <linux/leds.h>
#include <linux/regulator/driver.h>
/* Maximum current in uA according to IEEE 802.3-2022 Table 145-1 */
@@ -268,6 +269,21 @@ struct pse_pi {
};
/**
+ * struct pse_pi_led_triggers - LED trigger state for a PSE PI
+ *
+ * @delivering: LED trigger for power delivering state
+ * @enabled: LED trigger for admin enabled state
+ * @last_delivering: cached delivering state for change detection
+ * @last_enabled: cached enabled state for change detection
+ */
+struct pse_pi_led_triggers {
+ struct led_trigger delivering;
+ struct led_trigger enabled;
+ bool last_delivering;
+ bool last_enabled;
+};
+
+/**
* struct pse_ntf - PSE notification element
*
* @id: ID of the PSE control
@@ -305,6 +321,7 @@ struct pse_ntf {
* @ntf_work: workqueue for PSE notification management
* @ntf_fifo: PSE notifications FIFO
* @ntf_fifo_lock: protect @ntf_fifo writer
+ * @pi_led_trigs: per-PI LED trigger state array
*/
struct pse_controller_dev {
const struct pse_controller_ops *ops;
@@ -330,6 +347,7 @@ struct pse_controller_dev {
struct work_struct ntf_work;
DECLARE_KFIFO_PTR(ntf_fifo, struct pse_ntf);
spinlock_t ntf_fifo_lock; /* Protect @ntf_fifo writer */
+ struct pse_pi_led_triggers *pi_led_trigs;
};
/**
-7
View File
@@ -101,13 +101,6 @@ CONFIG_CRYPTO_CBC=y
CONFIG_CRYPTO_CRYPTD=y
CONFIG_CRYPTO_DEFLATE=y
CONFIG_CRYPTO_DES=y
CONFIG_CRYPTO_DEV_QCE=y
# CONFIG_CRYPTO_DEV_QCE_ENABLE_AEAD is not set
# CONFIG_CRYPTO_DEV_QCE_ENABLE_ALL is not set
# CONFIG_CRYPTO_DEV_QCE_ENABLE_SHA is not set
CONFIG_CRYPTO_DEV_QCE_ENABLE_SKCIPHER=y
CONFIG_CRYPTO_DEV_QCE_SKCIPHER=y
CONFIG_CRYPTO_DEV_QCE_SW_MAX_LEN=512
CONFIG_CRYPTO_DEV_QCOM_RNG=y
CONFIG_CRYPTO_DRBG=y
CONFIG_CRYPTO_DRBG_HMAC=y
@@ -0,0 +1,8 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for the IPQ ESS driver
#
obj-$(CONFIG_QCOM_IPQ4019_ESS_EDMA) += ipq_ess.o
ipq_ess-objs := ipqess.o ipqess_ethtool.o
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,518 @@
/* SPDX-License-Identifier: (GPL-2.0 OR ISC) */
/* Copyright (c) 2014 - 2016, The Linux Foundation. All rights reserved.
* Copyright (c) 2017 - 2018, John Crispin <john@phrozen.org>
* Copyright (c) 2018 - 2019, Christian Lamparter <chunkeey@gmail.com>
* Copyright (c) 2020 - 2021, Gabor Juhos <j4g8y7@gmail.com>
* Copyright (c) 2021 - 2022, Maxime Chevallier <maxime.chevallier@bootlin.com>
*
*/
#ifndef _IPQESS_H_
#define _IPQESS_H_
#define IPQESS_NETDEV_QUEUES 4
#define IPQESS_TPD_EOP_SHIFT 31
#define IPQESS_PORT_ID_SHIFT 12
#define IPQESS_PORT_ID_MASK 0x7
/* tpd word 3 bit 18-28 */
#define IPQESS_TPD_PORT_BITMAP_SHIFT 18
#define IPQESS_TPD_FROM_CPU_SHIFT 25
#define IPQESS_RX_RING_SIZE 128
#define IPQESS_RX_HEAD_BUFF_SIZE 1540
#define IPQESS_TX_RING_SIZE 128
#define IPQESS_MAX_RX_QUEUE 8
#define IPQESS_MAX_TX_QUEUE 16
/* Configurations */
#define IPQESS_INTR_CLEAR_TYPE 0
#define IPQESS_INTR_SW_IDX_W_TYPE 0
#define IPQESS_FIFO_THRESH_TYPE 0
#define IPQESS_RSS_TYPE 0
#define IPQESS_RX_IMT 0x0020
#define IPQESS_TX_IMT 0x0050
#define IPQESS_TPD_BURST 5
#define IPQESS_TXF_BURST 0x100
#define IPQESS_RFD_BURST 8
#define IPQESS_RFD_THR 16
#define IPQESS_RFD_LTHR 0
/* Flags used in transmit direction */
#define IPQESS_DESC_LAST 0x1
#define IPQESS_DESC_SINGLE 0x2
#define IPQESS_DESC_PAGE 0x4
struct ipqess_statistics {
u32 tx_q0_pkt;
u32 tx_q1_pkt;
u32 tx_q2_pkt;
u32 tx_q3_pkt;
u32 tx_q4_pkt;
u32 tx_q5_pkt;
u32 tx_q6_pkt;
u32 tx_q7_pkt;
u32 tx_q8_pkt;
u32 tx_q9_pkt;
u32 tx_q10_pkt;
u32 tx_q11_pkt;
u32 tx_q12_pkt;
u32 tx_q13_pkt;
u32 tx_q14_pkt;
u32 tx_q15_pkt;
u32 tx_q0_byte;
u32 tx_q1_byte;
u32 tx_q2_byte;
u32 tx_q3_byte;
u32 tx_q4_byte;
u32 tx_q5_byte;
u32 tx_q6_byte;
u32 tx_q7_byte;
u32 tx_q8_byte;
u32 tx_q9_byte;
u32 tx_q10_byte;
u32 tx_q11_byte;
u32 tx_q12_byte;
u32 tx_q13_byte;
u32 tx_q14_byte;
u32 tx_q15_byte;
u32 rx_q0_pkt;
u32 rx_q1_pkt;
u32 rx_q2_pkt;
u32 rx_q3_pkt;
u32 rx_q4_pkt;
u32 rx_q5_pkt;
u32 rx_q6_pkt;
u32 rx_q7_pkt;
u32 rx_q0_byte;
u32 rx_q1_byte;
u32 rx_q2_byte;
u32 rx_q3_byte;
u32 rx_q4_byte;
u32 rx_q5_byte;
u32 rx_q6_byte;
u32 rx_q7_byte;
u32 tx_desc_error;
};
struct ipqess_tx_desc {
__le16 len;
__le16 svlan_tag;
__le32 word1;
__le32 addr;
__le32 word3;
} __aligned(16) __packed;
struct ipqess_rx_desc {
__le16 rrd0;
__le16 rrd1;
__le16 rrd2;
__le16 rrd3;
__le16 rrd4;
__le16 rrd5;
__le16 rrd6;
__le16 rrd7;
} __aligned(16) __packed;
struct ipqess_buf {
struct sk_buff *skb;
dma_addr_t dma;
u32 flags;
u16 length;
};
struct ipqess_tx_ring {
struct napi_struct napi_tx;
u32 idx;
int ring_id;
struct ipqess *ess;
struct netdev_queue *nq;
struct ipqess_tx_desc *hw_desc;
struct ipqess_buf *buf;
dma_addr_t dma;
u16 count;
u16 head;
u16 tail;
};
struct ipqess_rx_ring {
struct napi_struct napi_rx;
u32 idx;
int ring_id;
struct ipqess *ess;
struct device *ppdev;
struct ipqess_rx_desc **hw_desc;
struct ipqess_buf *buf;
dma_addr_t dma;
u16 head;
u16 tail;
atomic_t refill_count;
};
struct ipqess_rx_ring_refill {
struct ipqess_rx_ring *rx_ring;
struct work_struct refill_work;
};
#define IPQESS_IRQ_NAME_LEN 32
struct ipqess {
struct net_device *netdev;
void __iomem *hw_addr;
struct clk *ess_clk;
struct reset_control *ess_rst;
struct ipqess_rx_ring rx_ring[IPQESS_NETDEV_QUEUES];
struct platform_device *pdev;
struct phylink *phylink;
struct phylink_config phylink_config;
struct ipqess_tx_ring tx_ring[IPQESS_NETDEV_QUEUES];
struct ipqess_statistics ipqess_stats;
/* Protects stats */
spinlock_t stats_lock;
struct net_device_stats stats;
struct ipqess_rx_ring_refill rx_refill[IPQESS_NETDEV_QUEUES];
u32 tx_irq[IPQESS_MAX_TX_QUEUE];
char tx_irq_names[IPQESS_MAX_TX_QUEUE][IPQESS_IRQ_NAME_LEN];
u32 rx_irq[IPQESS_MAX_RX_QUEUE];
char rx_irq_names[IPQESS_MAX_TX_QUEUE][IPQESS_IRQ_NAME_LEN];
};
void ipqess_set_ethtool_ops(struct net_device *netdev);
void ipqess_update_hw_stats(struct ipqess *ess);
/* register definition */
#define IPQESS_REG_MAS_CTRL 0x0
#define IPQESS_REG_TIMEOUT_CTRL 0x004
#define IPQESS_REG_DBG0 0x008
#define IPQESS_REG_DBG1 0x00C
#define IPQESS_REG_SW_CTRL0 0x100
#define IPQESS_REG_SW_CTRL1 0x104
/* Interrupt Status Register */
#define IPQESS_REG_RX_ISR 0x200
#define IPQESS_REG_TX_ISR 0x208
#define IPQESS_REG_MISC_ISR 0x210
#define IPQESS_REG_WOL_ISR 0x218
#define IPQESS_MISC_ISR_RX_URG_Q(x) (1 << (x))
#define IPQESS_MISC_ISR_AXIR_TIMEOUT 0x00000100
#define IPQESS_MISC_ISR_AXIR_ERR 0x00000200
#define IPQESS_MISC_ISR_TXF_DEAD 0x00000400
#define IPQESS_MISC_ISR_AXIW_ERR 0x00000800
#define IPQESS_MISC_ISR_AXIW_TIMEOUT 0x00001000
#define IPQESS_WOL_ISR 0x00000001
/* Interrupt Mask Register */
#define IPQESS_REG_MISC_IMR 0x214
#define IPQESS_REG_WOL_IMR 0x218
#define IPQESS_RX_IMR_NORMAL_MASK 0x1
#define IPQESS_TX_IMR_NORMAL_MASK 0x1
#define IPQESS_MISC_IMR_NORMAL_MASK 0x80001FFF
#define IPQESS_WOL_IMR_NORMAL_MASK 0x1
/* Edma receive consumer index */
#define IPQESS_REG_RX_SW_CONS_IDX_Q(x) (0x220 + ((x) << 2)) /* x is the queue id */
/* Edma transmit consumer index */
#define IPQESS_REG_TX_SW_CONS_IDX_Q(x) (0x240 + ((x) << 2)) /* x is the queue id */
/* IRQ Moderator Initial Timer Register */
#define IPQESS_REG_IRQ_MODRT_TIMER_INIT 0x280
#define IPQESS_IRQ_MODRT_TIMER_MASK 0xFFFF
#define IPQESS_IRQ_MODRT_RX_TIMER_SHIFT 0
#define IPQESS_IRQ_MODRT_TX_TIMER_SHIFT 16
/* Interrupt Control Register */
#define IPQESS_REG_INTR_CTRL 0x284
#define IPQESS_INTR_CLR_TYP_SHIFT 0
#define IPQESS_INTR_SW_IDX_W_TYP_SHIFT 1
#define IPQESS_INTR_CLEAR_TYPE_W1 0
#define IPQESS_INTR_CLEAR_TYPE_R 1
/* RX Interrupt Mask Register */
#define IPQESS_REG_RX_INT_MASK_Q(x) (0x300 + ((x) << 2)) /* x = queue id */
/* TX Interrupt mask register */
#define IPQESS_REG_TX_INT_MASK_Q(x) (0x340 + ((x) << 2)) /* x = queue id */
/* Load Ptr Register
* Software sets this bit after the initialization of the head and tail
*/
#define IPQESS_REG_TX_SRAM_PART 0x400
#define IPQESS_LOAD_PTR_SHIFT 16
/* TXQ Control Register */
#define IPQESS_REG_TXQ_CTRL 0x404
#define IPQESS_TXQ_CTRL_IP_OPTION_EN 0x10
#define IPQESS_TXQ_CTRL_TXQ_EN 0x20
#define IPQESS_TXQ_CTRL_ENH_MODE 0x40
#define IPQESS_TXQ_CTRL_LS_8023_EN 0x80
#define IPQESS_TXQ_CTRL_TPD_BURST_EN 0x100
#define IPQESS_TXQ_CTRL_LSO_BREAK_EN 0x200
#define IPQESS_TXQ_NUM_TPD_BURST_MASK 0xF
#define IPQESS_TXQ_TXF_BURST_NUM_MASK 0xFFFF
#define IPQESS_TXQ_NUM_TPD_BURST_SHIFT 0
#define IPQESS_TXQ_TXF_BURST_NUM_SHIFT 16
#define IPQESS_REG_TXF_WATER_MARK 0x408 /* In 8-bytes */
#define IPQESS_TXF_WATER_MARK_MASK 0x0FFF
#define IPQESS_TXF_LOW_WATER_MARK_SHIFT 0
#define IPQESS_TXF_HIGH_WATER_MARK_SHIFT 16
#define IPQESS_TXQ_CTRL_BURST_MODE_EN 0x80000000
/* WRR Control Register */
#define IPQESS_REG_WRR_CTRL_Q0_Q3 0x40c
#define IPQESS_REG_WRR_CTRL_Q4_Q7 0x410
#define IPQESS_REG_WRR_CTRL_Q8_Q11 0x414
#define IPQESS_REG_WRR_CTRL_Q12_Q15 0x418
/* Weight round robin(WRR), it takes queue as input, and computes
* starting bits where we need to write the weight for a particular
* queue
*/
#define IPQESS_WRR_SHIFT(x) (((x) * 5) % 20)
/* Tx Descriptor Control Register */
#define IPQESS_REG_TPD_RING_SIZE 0x41C
#define IPQESS_TPD_RING_SIZE_SHIFT 0
#define IPQESS_TPD_RING_SIZE_MASK 0xFFFF
/* Transmit descriptor base address */
#define IPQESS_REG_TPD_BASE_ADDR_Q(x) (0x420 + ((x) << 2)) /* x = queue id */
/* TPD Index Register */
#define IPQESS_REG_TPD_IDX_Q(x) (0x460 + ((x) << 2)) /* x = queue id */
#define IPQESS_TPD_PROD_IDX_BITS 0x0000FFFF
#define IPQESS_TPD_CONS_IDX_BITS 0xFFFF0000
#define IPQESS_TPD_PROD_IDX_MASK 0xFFFF
#define IPQESS_TPD_CONS_IDX_MASK 0xFFFF
#define IPQESS_TPD_PROD_IDX_SHIFT 0
#define IPQESS_TPD_CONS_IDX_SHIFT 16
/* TX Virtual Queue Mapping Control Register */
#define IPQESS_REG_VQ_CTRL0 0x4A0
#define IPQESS_REG_VQ_CTRL1 0x4A4
/* Virtual QID shift, it takes queue as input, and computes
* Virtual QID position in virtual qid control register
*/
#define IPQESS_VQ_ID_SHIFT(i) (((i) * 3) % 24)
/* Virtual Queue Default Value */
#define IPQESS_VQ_REG_VALUE 0x240240
/* Tx side Port Interface Control Register */
#define IPQESS_REG_PORT_CTRL 0x4A8
#define IPQESS_PAD_EN_SHIFT 15
/* Tx side VLAN Configuration Register */
#define IPQESS_REG_VLAN_CFG 0x4AC
#define IPQESS_VLAN_CFG_SVLAN_TPID_SHIFT 0
#define IPQESS_VLAN_CFG_SVLAN_TPID_MASK 0xffff
#define IPQESS_VLAN_CFG_CVLAN_TPID_SHIFT 16
#define IPQESS_VLAN_CFG_CVLAN_TPID_MASK 0xffff
#define IPQESS_TX_CVLAN 16
#define IPQESS_TX_INS_CVLAN 17
#define IPQESS_TX_CVLAN_TAG_SHIFT 0
#define IPQESS_TX_SVLAN 14
#define IPQESS_TX_INS_SVLAN 15
#define IPQESS_TX_SVLAN_TAG_SHIFT 16
/* Tx Queue Packet Statistic Register */
#define IPQESS_REG_TX_STAT_PKT_Q(x) (0x700 + ((x) << 3)) /* x = queue id */
#define IPQESS_TX_STAT_PKT_MASK 0xFFFFFF
/* Tx Queue Byte Statistic Register */
#define IPQESS_REG_TX_STAT_BYTE_Q(x) (0x704 + ((x) << 3)) /* x = queue id */
/* Load Balance Based Ring Offset Register */
#define IPQESS_REG_LB_RING 0x800
#define IPQESS_LB_RING_ENTRY_MASK 0xff
#define IPQESS_LB_RING_ID_MASK 0x7
#define IPQESS_LB_RING_PROFILE_ID_MASK 0x3
#define IPQESS_LB_RING_ENTRY_BIT_OFFSET 8
#define IPQESS_LB_RING_ID_OFFSET 0
#define IPQESS_LB_RING_PROFILE_ID_OFFSET 3
#define IPQESS_LB_REG_VALUE 0x6040200
/* Load Balance Priority Mapping Register */
#define IPQESS_REG_LB_PRI_START 0x804
#define IPQESS_REG_LB_PRI_END 0x810
#define IPQESS_LB_PRI_REG_INC 4
#define IPQESS_LB_PRI_ENTRY_BIT_OFFSET 4
#define IPQESS_LB_PRI_ENTRY_MASK 0xf
/* RSS Priority Mapping Register */
#define IPQESS_REG_RSS_PRI 0x820
#define IPQESS_RSS_PRI_ENTRY_MASK 0xf
#define IPQESS_RSS_RING_ID_MASK 0x7
#define IPQESS_RSS_PRI_ENTRY_BIT_OFFSET 4
/* RSS Indirection Register */
#define IPQESS_REG_RSS_IDT(x) (0x840 + ((x) << 2)) /* x = No. of indirection table */
#define IPQESS_NUM_IDT 16
#define IPQESS_RSS_IDT_VALUE 0x64206420
/* Default RSS Ring Register */
#define IPQESS_REG_DEF_RSS 0x890
#define IPQESS_DEF_RSS_MASK 0x7
/* RSS Hash Function Type Register */
#define IPQESS_REG_RSS_TYPE 0x894
#define IPQESS_RSS_TYPE_NONE 0x01
#define IPQESS_RSS_TYPE_IPV4TCP 0x02
#define IPQESS_RSS_TYPE_IPV6_TCP 0x04
#define IPQESS_RSS_TYPE_IPV4_UDP 0x08
#define IPQESS_RSS_TYPE_IPV6UDP 0x10
#define IPQESS_RSS_TYPE_IPV4 0x20
#define IPQESS_RSS_TYPE_IPV6 0x40
#define IPQESS_RSS_HASH_MODE_MASK 0x7f
#define IPQESS_REG_RSS_HASH_VALUE 0x8C0
#define IPQESS_REG_RSS_TYPE_RESULT 0x8C4
#define IPQESS_HASH_TYPE_START 0
#define IPQESS_HASH_TYPE_END 5
#define IPQESS_HASH_TYPE_SHIFT 12
#define IPQESS_RFS_FLOW_ENTRIES 1024
#define IPQESS_RFS_FLOW_ENTRIES_MASK (IPQESS_RFS_FLOW_ENTRIES - 1)
#define IPQESS_RFS_EXPIRE_COUNT_PER_CALL 128
/* RFD Base Address Register */
#define IPQESS_REG_RFD_BASE_ADDR_Q(x) (0x950 + ((x) << 2)) /* x = queue id */
/* RFD Index Register */
#define IPQESS_REG_RFD_IDX_Q(x) (0x9B0 + ((x) << 2)) /* x = queue id */
#define IPQESS_RFD_PROD_IDX_BITS 0x00000FFF
#define IPQESS_RFD_CONS_IDX_BITS 0x0FFF0000
#define IPQESS_RFD_PROD_IDX_MASK 0xFFF
#define IPQESS_RFD_CONS_IDX_MASK 0xFFF
#define IPQESS_RFD_PROD_IDX_SHIFT 0
#define IPQESS_RFD_CONS_IDX_SHIFT 16
/* Rx Descriptor Control Register */
#define IPQESS_REG_RX_DESC0 0xA10
#define IPQESS_RFD_RING_SIZE_MASK 0xFFF
#define IPQESS_RX_BUF_SIZE_MASK 0xFFFF
#define IPQESS_RFD_RING_SIZE_SHIFT 0
#define IPQESS_RX_BUF_SIZE_SHIFT 16
#define IPQESS_REG_RX_DESC1 0xA14
#define IPQESS_RXQ_RFD_BURST_NUM_MASK 0x3F
#define IPQESS_RXQ_RFD_PF_THRESH_MASK 0x1F
#define IPQESS_RXQ_RFD_LOW_THRESH_MASK 0xFFF
#define IPQESS_RXQ_RFD_BURST_NUM_SHIFT 0
#define IPQESS_RXQ_RFD_PF_THRESH_SHIFT 8
#define IPQESS_RXQ_RFD_LOW_THRESH_SHIFT 16
/* RXQ Control Register */
#define IPQESS_REG_RXQ_CTRL 0xA18
#define IPQESS_FIFO_THRESH_TYPE_SHIF 0
#define IPQESS_FIFO_THRESH_128_BYTE 0x0
#define IPQESS_FIFO_THRESH_64_BYTE 0x1
#define IPQESS_RXQ_CTRL_RMV_VLAN 0x00000002
#define IPQESS_RXQ_CTRL_EN_MASK GENMASK(15, 8)
#define IPQESS_RXQ_CTRL_EN(__qid) BIT(8 + (__qid))
/* AXI Burst Size Config */
#define IPQESS_REG_AXIW_CTRL_MAXWRSIZE 0xA1C
#define IPQESS_AXIW_MAXWRSIZE_VALUE 0x0
/* Rx Statistics Register */
#define IPQESS_REG_RX_STAT_BYTE_Q(x) (0xA30 + ((x) << 2)) /* x = queue id */
#define IPQESS_REG_RX_STAT_PKT_Q(x) (0xA50 + ((x) << 2)) /* x = queue id */
/* WoL Pattern Length Register */
#define IPQESS_REG_WOL_PATTERN_LEN0 0xC00
#define IPQESS_WOL_PT_LEN_MASK 0xFF
#define IPQESS_WOL_PT0_LEN_SHIFT 0
#define IPQESS_WOL_PT1_LEN_SHIFT 8
#define IPQESS_WOL_PT2_LEN_SHIFT 16
#define IPQESS_WOL_PT3_LEN_SHIFT 24
#define IPQESS_REG_WOL_PATTERN_LEN1 0xC04
#define IPQESS_WOL_PT4_LEN_SHIFT 0
#define IPQESS_WOL_PT5_LEN_SHIFT 8
#define IPQESS_WOL_PT6_LEN_SHIFT 16
/* WoL Control Register */
#define IPQESS_REG_WOL_CTRL 0xC08
#define IPQESS_WOL_WK_EN 0x00000001
#define IPQESS_WOL_MG_EN 0x00000002
#define IPQESS_WOL_PT0_EN 0x00000004
#define IPQESS_WOL_PT1_EN 0x00000008
#define IPQESS_WOL_PT2_EN 0x00000010
#define IPQESS_WOL_PT3_EN 0x00000020
#define IPQESS_WOL_PT4_EN 0x00000040
#define IPQESS_WOL_PT5_EN 0x00000080
#define IPQESS_WOL_PT6_EN 0x00000100
/* MAC Control Register */
#define IPQESS_REG_MAC_CTRL0 0xC20
#define IPQESS_REG_MAC_CTRL1 0xC24
/* WoL Pattern Register */
#define IPQESS_REG_WOL_PATTERN_START 0x5000
#define IPQESS_PATTERN_PART_REG_OFFSET 0x40
/* TX descriptor fields */
#define IPQESS_TPD_HDR_SHIFT 0
#define IPQESS_TPD_PPPOE_EN 0x00000100
#define IPQESS_TPD_IP_CSUM_EN 0x00000200
#define IPQESS_TPD_TCP_CSUM_EN 0x0000400
#define IPQESS_TPD_UDP_CSUM_EN 0x00000800
#define IPQESS_TPD_CUSTOM_CSUM_EN 0x00000C00
#define IPQESS_TPD_LSO_EN 0x00001000
#define IPQESS_TPD_LSO_V2_EN 0x00002000
/* The VLAN_TAGGED bit is not used in the publicly available
* drivers. The definition has been stolen from the Atheros
* 'alx' driver (drivers/net/ethernet/atheros/alx/hw.h). It
* seems that it has the same meaning in regard to the EDMA
* hardware.
*/
#define IPQESS_TPD_VLAN_TAGGED 0x00004000
#define IPQESS_TPD_IPV4_EN 0x00010000
#define IPQESS_TPD_MSS_MASK 0x1FFF
#define IPQESS_TPD_MSS_SHIFT 18
#define IPQESS_TPD_CUSTOM_CSUM_SHIFT 18
/* RRD descriptor fields */
#define IPQESS_RRD_NUM_RFD_MASK 0x000F
#define IPQESS_RRD_PKT_SIZE_MASK 0x3FFF
#define IPQESS_RRD_SRC_PORT_NUM_MASK 0x4000
#define IPQESS_RRD_SVLAN 0x8000
#define IPQESS_RRD_FLOW_COOKIE_MASK 0x07FF
#define IPQESS_RRD_PKT_SIZE_MASK 0x3FFF
#define IPQESS_RRD_CSUM_FAIL_MASK 0xC000
#define IPQESS_RRD_CVLAN 0x0001
#define IPQESS_RRD_DESC_VALID 0x8000
#define IPQESS_RRD_PRIORITY_SHIFT 4
#define IPQESS_RRD_PRIORITY_MASK 0x7
#define IPQESS_RRD_PORT_TYPE_SHIFT 7
#define IPQESS_RRD_PORT_TYPE_MASK 0x1F
#define IPQESS_RRD_PORT_ID_MASK 0x7000
#endif
@@ -0,0 +1,164 @@
// SPDX-License-Identifier: GPL-2.0 OR ISC
/* Copyright (c) 2015 - 2016, The Linux Foundation. All rights reserved.
* Copyright (c) 2017 - 2018, John Crispin <john@phrozen.org>
* Copyright (c) 2021 - 2022, Maxime Chevallier <maxime.chevallier@bootlin.com>
*
*/
#include <linux/ethtool.h>
#include <linux/netdevice.h>
#include <linux/string.h>
#include <linux/phylink.h>
#include "ipqess.h"
struct ipqess_ethtool_stats {
u8 string[ETH_GSTRING_LEN];
u32 offset;
};
#define IPQESS_STAT(m) offsetof(struct ipqess_statistics, m)
#define DRVINFO_LEN 32
static const struct ipqess_ethtool_stats ipqess_stats[] = {
{"tx_q0_pkt", IPQESS_STAT(tx_q0_pkt)},
{"tx_q1_pkt", IPQESS_STAT(tx_q1_pkt)},
{"tx_q2_pkt", IPQESS_STAT(tx_q2_pkt)},
{"tx_q3_pkt", IPQESS_STAT(tx_q3_pkt)},
{"tx_q4_pkt", IPQESS_STAT(tx_q4_pkt)},
{"tx_q5_pkt", IPQESS_STAT(tx_q5_pkt)},
{"tx_q6_pkt", IPQESS_STAT(tx_q6_pkt)},
{"tx_q7_pkt", IPQESS_STAT(tx_q7_pkt)},
{"tx_q8_pkt", IPQESS_STAT(tx_q8_pkt)},
{"tx_q9_pkt", IPQESS_STAT(tx_q9_pkt)},
{"tx_q10_pkt", IPQESS_STAT(tx_q10_pkt)},
{"tx_q11_pkt", IPQESS_STAT(tx_q11_pkt)},
{"tx_q12_pkt", IPQESS_STAT(tx_q12_pkt)},
{"tx_q13_pkt", IPQESS_STAT(tx_q13_pkt)},
{"tx_q14_pkt", IPQESS_STAT(tx_q14_pkt)},
{"tx_q15_pkt", IPQESS_STAT(tx_q15_pkt)},
{"tx_q0_byte", IPQESS_STAT(tx_q0_byte)},
{"tx_q1_byte", IPQESS_STAT(tx_q1_byte)},
{"tx_q2_byte", IPQESS_STAT(tx_q2_byte)},
{"tx_q3_byte", IPQESS_STAT(tx_q3_byte)},
{"tx_q4_byte", IPQESS_STAT(tx_q4_byte)},
{"tx_q5_byte", IPQESS_STAT(tx_q5_byte)},
{"tx_q6_byte", IPQESS_STAT(tx_q6_byte)},
{"tx_q7_byte", IPQESS_STAT(tx_q7_byte)},
{"tx_q8_byte", IPQESS_STAT(tx_q8_byte)},
{"tx_q9_byte", IPQESS_STAT(tx_q9_byte)},
{"tx_q10_byte", IPQESS_STAT(tx_q10_byte)},
{"tx_q11_byte", IPQESS_STAT(tx_q11_byte)},
{"tx_q12_byte", IPQESS_STAT(tx_q12_byte)},
{"tx_q13_byte", IPQESS_STAT(tx_q13_byte)},
{"tx_q14_byte", IPQESS_STAT(tx_q14_byte)},
{"tx_q15_byte", IPQESS_STAT(tx_q15_byte)},
{"rx_q0_pkt", IPQESS_STAT(rx_q0_pkt)},
{"rx_q1_pkt", IPQESS_STAT(rx_q1_pkt)},
{"rx_q2_pkt", IPQESS_STAT(rx_q2_pkt)},
{"rx_q3_pkt", IPQESS_STAT(rx_q3_pkt)},
{"rx_q4_pkt", IPQESS_STAT(rx_q4_pkt)},
{"rx_q5_pkt", IPQESS_STAT(rx_q5_pkt)},
{"rx_q6_pkt", IPQESS_STAT(rx_q6_pkt)},
{"rx_q7_pkt", IPQESS_STAT(rx_q7_pkt)},
{"rx_q0_byte", IPQESS_STAT(rx_q0_byte)},
{"rx_q1_byte", IPQESS_STAT(rx_q1_byte)},
{"rx_q2_byte", IPQESS_STAT(rx_q2_byte)},
{"rx_q3_byte", IPQESS_STAT(rx_q3_byte)},
{"rx_q4_byte", IPQESS_STAT(rx_q4_byte)},
{"rx_q5_byte", IPQESS_STAT(rx_q5_byte)},
{"rx_q6_byte", IPQESS_STAT(rx_q6_byte)},
{"rx_q7_byte", IPQESS_STAT(rx_q7_byte)},
{"tx_desc_error", IPQESS_STAT(tx_desc_error)},
};
static int ipqess_get_strset_count(struct net_device *netdev, int sset)
{
switch (sset) {
case ETH_SS_STATS:
return ARRAY_SIZE(ipqess_stats);
default:
netdev_dbg(netdev, "%s: Unsupported string set", __func__);
return -EOPNOTSUPP;
}
}
static void ipqess_get_strings(struct net_device *netdev, u32 stringset,
u8 *data)
{
u8 *p = data;
u32 i;
switch (stringset) {
case ETH_SS_STATS:
for (i = 0; i < ARRAY_SIZE(ipqess_stats); i++)
ethtool_puts(&p, ipqess_stats[i].string);
break;
}
}
static void ipqess_get_ethtool_stats(struct net_device *netdev,
struct ethtool_stats *stats,
uint64_t *data)
{
struct ipqess *ess = netdev_priv(netdev);
u32 *essstats = (u32 *)&ess->ipqess_stats;
int i;
spin_lock(&ess->stats_lock);
ipqess_update_hw_stats(ess);
for (i = 0; i < ARRAY_SIZE(ipqess_stats); i++)
data[i] = *(u32 *)(essstats + (ipqess_stats[i].offset / sizeof(u32)));
spin_unlock(&ess->stats_lock);
}
static void ipqess_get_drvinfo(struct net_device *dev,
struct ethtool_drvinfo *info)
{
strscpy(info->driver, "qca_ipqess", DRVINFO_LEN);
strscpy(info->bus_info, "axi", ETHTOOL_BUSINFO_LEN);
}
static int ipqess_get_link_ksettings(struct net_device *netdev,
struct ethtool_link_ksettings *cmd)
{
struct ipqess *ess = netdev_priv(netdev);
return phylink_ethtool_ksettings_get(ess->phylink, cmd);
}
static int ipqess_set_link_ksettings(struct net_device *netdev,
const struct ethtool_link_ksettings *cmd)
{
struct ipqess *ess = netdev_priv(netdev);
return phylink_ethtool_ksettings_set(ess->phylink, cmd);
}
static void ipqess_get_ringparam(struct net_device *netdev,
struct ethtool_ringparam *ring,
struct kernel_ethtool_ringparam *kernel_ering,
struct netlink_ext_ack *extack)
{
ring->tx_max_pending = IPQESS_TX_RING_SIZE;
ring->rx_max_pending = IPQESS_RX_RING_SIZE;
}
static const struct ethtool_ops ipqesstool_ops = {
.get_drvinfo = &ipqess_get_drvinfo,
.get_link = &ethtool_op_get_link,
.get_link_ksettings = &ipqess_get_link_ksettings,
.set_link_ksettings = &ipqess_set_link_ksettings,
.get_strings = &ipqess_get_strings,
.get_sset_count = &ipqess_get_strset_count,
.get_ethtool_stats = &ipqess_get_ethtool_stats,
.get_ringparam = ipqess_get_ringparam,
};
void ipqess_set_ethtool_ops(struct net_device *netdev)
{
netdev->ethtool_ops = &ipqesstool_ops;
}
@@ -64,7 +64,7 @@ Signed-off-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21146,6 +21146,7 @@ L: netdev@vger.kernel.org
@@ -21145,6 +21145,7 @@ L: netdev@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/net/qcom,ipq4019-ess-edma.yaml
F: drivers/net/ethernet/qualcomm/ipqess/

Some files were not shown because too many files have changed in this diff Show More