diff --git a/include/toplevel.mk b/include/toplevel.mk index c7e71ad571..c233a4c214 100644 --- a/include/toplevel.mk +++ b/include/toplevel.mk @@ -91,6 +91,7 @@ prepare-tmpinfo: FORCE [ tmp/.config-feeds.in -nt tmp/.packageauxvars ] || ./scripts/feeds feed_config > tmp/.config-feeds.in ./scripts/package-metadata.pl mk tmp/.packageinfo > tmp/.packagedeps || { rm -f tmp/.packagedeps; false; } ./scripts/package-metadata.pl pkgaux tmp/.packageinfo > tmp/.packageauxvars || { rm -f tmp/.packageauxvars; false; } + ./scripts/package-metadata.pl usergroup tmp/.packageinfo > tmp/.packageusergroup || { rm -f tmp/.packageusergroup; false; } touch $(TOPDIR)/tmp/.build .config: ./scripts/config/conf $(if $(CONFIG_HAVE_DOT_CONFIG),,prepare-tmpinfo) diff --git a/package/boot/uboot-envtools/Makefile b/package/boot/uboot-envtools/Makefile index 7d464f8d08..473704053c 100644 --- a/package/boot/uboot-envtools/Makefile +++ b/package/boot/uboot-envtools/Makefile @@ -10,7 +10,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=uboot-envtools PKG_DISTNAME:=u-boot PKG_VERSION:=2020.04 -PKG_RELEASE:=3 +PKG_RELEASE:=4 PKG_SOURCE:=$(PKG_DISTNAME)-$(PKG_VERSION).tar.bz2 PKG_SOURCE_URL:= \ diff --git a/package/boot/uboot-envtools/files/mvebu b/package/boot/uboot-envtools/files/mvebu index 8ed1f87ead..1d3b79b962 100644 --- a/package/boot/uboot-envtools/files/mvebu +++ b/package/boot/uboot-envtools/files/mvebu @@ -15,7 +15,11 @@ board=$(board_name) case "$board" in cznic,turris-omnia) - ubootenv_add_uci_config "/dev/mtd0" "0xC0000" "0x10000" "0x40000" + if grep -q 'U-Boot 2015.10-rc2' /dev/mtd0; then + ubootenv_add_uci_config "/dev/mtd0" "0xc0000" "0x10000" "0x40000" + else + ubootenv_add_uci_config "/dev/mtd0" "0xf0000" "0x10000" "0x10000" + fi ;; glinet,gl-mv1000) ubootenv_add_uci_config "/dev/mtd1" "0x0" "0x8000" "0x8000" "1" diff --git a/package/kernel/hwmon-gsc/Makefile b/package/kernel/hwmon-gsc/Makefile index 95f6fab4c0..8cf4e0ee79 100644 --- a/package/kernel/hwmon-gsc/Makefile +++ b/package/kernel/hwmon-gsc/Makefile @@ -8,7 +8,7 @@ include $(INCLUDE_DIR)/package.mk define KernelPackage/hwmon-gsc SUBMENU:=Hardware Monitoring Support - DEPENDS:=@TARGET_imx6||TARGET_cns3xxx +kmod-hwmon-core +kmod-i2c-core + DEPENDS:=@TARGET_imx6 +kmod-hwmon-core +kmod-i2c-core TITLE:=Driver for the Gateworks System Controller AUTOLOAD:=$(call AutoLoad,60,gsc) FILES:=$(PKG_BUILD_DIR)/gsc.ko diff --git a/package/network/services/uhttpd/Makefile b/package/network/services/uhttpd/Makefile index 88478927cc..697ffc10b2 100644 --- a/package/network/services/uhttpd/Makefile +++ b/package/network/services/uhttpd/Makefile @@ -12,9 +12,9 @@ PKG_RELEASE:=1 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL=$(PROJECT_GIT)/project/uhttpd.git -PKG_SOURCE_DATE:=2020-08-05 -PKG_SOURCE_VERSION:=212f8364d49c1bc27dd8bdc394fc3615ea9b7ba3 -PKG_MIRROR_HASH:=c717924cd075951846cb8ad271f4754db56af53a271ed0afbdc5a2f482e15f97 +PKG_SOURCE_DATE:=2020-09-15 +PKG_SOURCE_VERSION:=11723570af9cb7bd87842e79c85ee99530be9902 +PKG_MIRROR_HASH:=39aa6d700689419db8e8f9bf59a4b9c9539c3ee636e0befebf38476bc883cc18 PKG_MAINTAINER:=Felix Fietkau PKG_LICENSE:=ISC diff --git a/scripts/ipkg-build b/scripts/ipkg-build index e3a9a882cf..343aab6caf 100755 --- a/scripts/ipkg-build +++ b/scripts/ipkg-build @@ -68,6 +68,26 @@ pkg_appears_sane() { return $PKG_ERROR } +resolve_file_mode_id() { + local var=$1 type=$2 name=$3 id + + case "$name" in + root) + id=0 + ;; + *[!0-9]*) + id=$(sed -ne "s#^$type $name \\([0-9]\\+\\)\\b.*\$#\\1#p" "$TOPDIR/tmp/.packageusergroup" 2>/dev/null) + ;; + *) + id=$name + ;; + esac + + export "$var=$id" + + [ -n "$id" ] +} + ### # ipkg-build "main" ### @@ -101,7 +121,7 @@ case $# in ;; *) echo $usage >&2 - exit 1 + exit 1 ;; esac @@ -141,11 +161,22 @@ for file_mode in $file_modes; do exit 1 ;; esac - path=$(echo "$file_mode" | cut -d ':' -f 1) - user_group=$(echo "$file_mode" | cut -d ':' -f 2-3) - mode=$(echo "$file_mode" | cut -d ':' -f 4) - chown "$user_group" "$pkg_dir/$path" + mode=${file_mode##*:}; path=${file_mode%:*} + group=${path##*:}; path=${path%:*} + user=${path##*:}; path=${path%:*} + + if ! resolve_file_mode_id uid user "$user"; then + echo "ERROR: unable to resolve uid of $user" >&2 + exit 1 + fi + + if ! resolve_file_mode_id gid group "$group"; then + echo "ERROR: unable to resolve gid of $group" >&2 + exit 1 + fi + + chown "$uid:$gid" "$pkg_dir/$path" chmod "$mode" "$pkg_dir/$path" done $TAR -X $tmp_dir/tarX --format=gnu --sort=name -cpf - --mtime="$TIMESTAMP" . | $GZIP -n - > $tmp_dir/data.tar.gz diff --git a/target/linux/cns3xxx/Makefile b/target/linux/cns3xxx/Makefile deleted file mode 100644 index 04df322b64..0000000000 --- a/target/linux/cns3xxx/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -# -# Copyright (C) 2010-2012 OpenWrt.org -# -# This is free software, licensed under the GNU General Public License v2. -# See /LICENSE for more information. -# -include $(TOPDIR)/rules.mk - -ARCH:=arm -BOARD:=cns3xxx -BOARDNAME:=Cavium Networks Econa CNS3xxx -FEATURES:=squashfs fpu gpio pcie usb usbgadget -CPU_TYPE:=mpcore -CPU_SUBTYPE:=vfp -KERNEL_PATCHVER:=4.19 - -define Target/Description - Build images for Cavium Networks Econa CNS3xxx based boards, - eg. the Gateworks Laguna family -endef - -include $(INCLUDE_DIR)/target.mk - -KERNELNAME:=zImage - -DEFAULT_PACKAGES += kmod-ath9k kmod-usb2 wpad-basic-wolfssl - -$(eval $(call BuildTarget)) diff --git a/target/linux/cns3xxx/base-files/etc/init.d/netdev-cpu b/target/linux/cns3xxx/base-files/etc/init.d/netdev-cpu deleted file mode 100755 index 3a2a57bc1e..0000000000 --- a/target/linux/cns3xxx/base-files/etc/init.d/netdev-cpu +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh /etc/rc.common - -START=99 - -get_irq() { - local name="$1" - grep -m 1 "$name" /proc/interrupts | cut -d: -f1 | sed 's, *,,' -} - -set_irq_affinity() { - local name="$1" - local val="$2" - local irq="$(get_irq "$name")" - [ -n "$irq" ] || return - echo "$val" > "/proc/irq/$irq/smp_affinity" -} - -start() { - set_irq_affinity gig_switch 2 - set_irq_affinity gig_stat 2 -} diff --git a/target/linux/cns3xxx/base-files/lib/cns3xxx.sh b/target/linux/cns3xxx/base-files/lib/cns3xxx.sh deleted file mode 100644 index ec694c4f7a..0000000000 --- a/target/linux/cns3xxx/base-files/lib/cns3xxx.sh +++ /dev/null @@ -1,26 +0,0 @@ -CNS3XXX_BOARD_NAME= -CNS3XXX_MODEL= - -cns3xxx_board_detect() { - local machine - local name - - machine=$(awk 'BEGIN{FS="[ \t]+:[ \t]"} /Hardware/ {print $2}' /proc/cpuinfo) - - case "$machine" in - "Gateworks Corporation Laguna"*) - name="laguna" - ;; - *) - name="generic"; - ;; - esac - - [ -z "$CNS3XXX_BOARD_NAME" ] && CNS3XXX_BOARD_NAME="$name" - [ -z "$CNS3XXX_MODEL" ] && CNS3XXX_MODEL="$machine" - - [ -e "/tmp/sysinfo/" ] || mkdir -p "/tmp/sysinfo/" - - echo "$CNS3XXX_BOARD_NAME" > /tmp/sysinfo/board_name - echo "$CNS3XXX_MODEL" > /tmp/sysinfo/model -} diff --git a/target/linux/cns3xxx/base-files/lib/preinit/01_sysinfo b/target/linux/cns3xxx/base-files/lib/preinit/01_sysinfo deleted file mode 100644 index 155fee42e0..0000000000 --- a/target/linux/cns3xxx/base-files/lib/preinit/01_sysinfo +++ /dev/null @@ -1,7 +0,0 @@ -do_sysinfo_cns3xxx() { - . /lib/cns3xxx.sh - - cns3xxx_board_detect -} - -boot_hook_add preinit_main do_sysinfo_cns3xxx diff --git a/target/linux/cns3xxx/base-files/lib/upgrade/platform.sh b/target/linux/cns3xxx/base-files/lib/upgrade/platform.sh deleted file mode 100644 index 58dd6d7590..0000000000 --- a/target/linux/cns3xxx/base-files/lib/upgrade/platform.sh +++ /dev/null @@ -1,19 +0,0 @@ -PART_NAME=firmware - -CI_BLKSZ=65536 - -platform_check_image() { - local magic="$(get_magic_long "$1")" - - [ "$#" -gt 1 ] && return 1 - - [ "$magic" != "27051956" ] && { - echo "Invalid image type." - return 1 - } - return 0 -} - -platform_do_upgrade() { - default_do_upgrade "$1" -} diff --git a/target/linux/cns3xxx/config-4.19 b/target/linux/cns3xxx/config-4.19 deleted file mode 100644 index 9b9aea461b..0000000000 --- a/target/linux/cns3xxx/config-4.19 +++ /dev/null @@ -1,368 +0,0 @@ -# CONFIG_AIO is not set -CONFIG_ALIGNMENT_TRAP=y -CONFIG_ARCH_CLOCKSOURCE_DATA=y -CONFIG_ARCH_CNS3XXX=y -CONFIG_ARCH_HAS_DEBUG_VIRTUAL=y -CONFIG_ARCH_HAS_ELF_RANDOMIZE=y -CONFIG_ARCH_HAS_FORTIFY_SOURCE=y -CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y -CONFIG_ARCH_HAS_KCOV=y -CONFIG_ARCH_HAS_MEMBARRIER_SYNC_CORE=y -CONFIG_ARCH_HAS_PHYS_TO_DMA=y -CONFIG_ARCH_HAS_SET_MEMORY=y -CONFIG_ARCH_HAS_SG_CHAIN=y -CONFIG_ARCH_HAS_STRICT_KERNEL_RWX=y -CONFIG_ARCH_HAS_STRICT_MODULE_RWX=y -CONFIG_ARCH_HAS_TICK_BROADCAST=y -CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y -CONFIG_ARCH_HIBERNATION_POSSIBLE=y -CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y -CONFIG_ARCH_MULTIPLATFORM=y -CONFIG_ARCH_MULTI_V6=y -CONFIG_ARCH_MULTI_V6_V7=y -CONFIG_ARCH_NR_GPIO=0 -CONFIG_ARCH_OPTIONAL_KERNEL_RWX=y -CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y -CONFIG_ARCH_SUPPORTS_UPROBES=y -CONFIG_ARCH_SUSPEND_POSSIBLE=y -CONFIG_ARCH_USE_BUILTIN_BSWAP=y -CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y -CONFIG_ARCH_WANT_GENERAL_HUGETLB=y -CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y -CONFIG_ARM=y -CONFIG_ARM_GIC=y -CONFIG_ARM_HAS_SG_CHAIN=y -CONFIG_ARM_HEAVY_MB=y -CONFIG_ARM_L1_CACHE_SHIFT=5 -CONFIG_ARM_PATCH_PHYS_VIRT=y -CONFIG_ARM_THUMB=y -CONFIG_ATA=y -CONFIG_ATAGS=y -# CONFIG_ATA_SFF is not set -CONFIG_ATA_VERBOSE_ERROR=y -CONFIG_AUTO_ZRELADDR=y -CONFIG_BCM_NET_PHYLIB=y -CONFIG_BLK_DEV_SD=y -CONFIG_BLK_MQ_PCI=y -CONFIG_BLK_SCSI_REQUEST=y -CONFIG_BROADCOM_PHY=y -CONFIG_CACHE_L2X0=y -CONFIG_CLKDEV_LOOKUP=y -CONFIG_CLONE_BACKWARDS=y -CONFIG_CMDLINE="console=ttyS0,115200" -CONFIG_CMDLINE_FORCE=y -CONFIG_CNS3XXX_ETH=y -CONFIG_COMMON_CLK=y -CONFIG_COREDUMP=y -CONFIG_CPU_32v6=y -CONFIG_CPU_32v6K=y -CONFIG_CPU_ABRT_EV6=y -# CONFIG_CPU_BPREDICT_DISABLE is not set -CONFIG_CPU_CACHE_V6=y -CONFIG_CPU_CACHE_VIPT=y -CONFIG_CPU_COPY_V6=y -CONFIG_CPU_CP15=y -CONFIG_CPU_CP15_MMU=y -CONFIG_CPU_HAS_ASID=y -# CONFIG_CPU_ICACHE_DISABLE is not set -CONFIG_CPU_PABRT_V6=y -CONFIG_CPU_RMAP=y -CONFIG_CPU_THUMB_CAPABLE=y -CONFIG_CPU_TLB_V6=y -CONFIG_CPU_V6K=y -CONFIG_CRASH_DUMP=y -CONFIG_CRC_CCITT=m -CONFIG_CRYPTO_AEAD=y -CONFIG_CRYPTO_AEAD2=y -CONFIG_CRYPTO_HASH2=y -CONFIG_CRYPTO_MANAGER=y -CONFIG_CRYPTO_MANAGER2=y -CONFIG_CRYPTO_NULL2=y -CONFIG_CRYPTO_RNG2=y -CONFIG_CRYPTO_WORKQUEUE=y -CONFIG_DCACHE_WORD_ACCESS=y -CONFIG_DEBUG_ALIGN_RODATA=y -CONFIG_DEBUG_BUGVERBOSE=y -CONFIG_DEBUG_INFO=y -CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" -# CONFIG_DEBUG_USER is not set -CONFIG_DMA_CACHE_FIQ_BROADCAST=y -CONFIG_DTC=y -CONFIG_EDAC_ATOMIC_SCRUB=y -CONFIG_EDAC_SUPPORT=y -CONFIG_EEPROM_AT24=y -CONFIG_ELF_CORE=y -CONFIG_FIQ=y -CONFIG_FIXED_PHY=y -CONFIG_FIX_EARLYCON_MEM=y -CONFIG_FRAME_POINTER=y -CONFIG_GENERIC_ALLOCATOR=y -CONFIG_GENERIC_BUG=y -CONFIG_GENERIC_CLOCKEVENTS=y -CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y -CONFIG_GENERIC_CPU_AUTOPROBE=y -CONFIG_GENERIC_EARLY_IOREMAP=y -CONFIG_GENERIC_IDLE_POLL_SETUP=y -CONFIG_GENERIC_IRQ_CHIP=y -CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y -CONFIG_GENERIC_IRQ_MULTI_HANDLER=y -CONFIG_GENERIC_IRQ_SHOW=y -CONFIG_GENERIC_IRQ_SHOW_LEVEL=y -CONFIG_GENERIC_PCI_IOMAP=y -CONFIG_GENERIC_SCHED_CLOCK=y -CONFIG_GENERIC_SMP_IDLE_THREAD=y -CONFIG_GENERIC_STRNCPY_FROM_USER=y -CONFIG_GENERIC_STRNLEN_USER=y -CONFIG_GLOB=y -CONFIG_GPIOLIB=y -CONFIG_GPIOLIB_IRQCHIP=y -CONFIG_GPIO_PCA953X=y -CONFIG_GPIO_PCA953X_IRQ=y -CONFIG_HANDLE_DOMAIN_IRQ=y -CONFIG_HARDIRQS_SW_RESEND=y -CONFIG_HAS_DMA=y -CONFIG_HAS_IOMEM=y -CONFIG_HAS_IOPORT_MAP=y -CONFIG_HAVE_ARCH_AUDITSYSCALL=y -CONFIG_HAVE_ARCH_JUMP_LABEL=y -CONFIG_HAVE_ARCH_KGDB=y -CONFIG_HAVE_ARCH_PFN_VALID=y -CONFIG_HAVE_ARCH_SECCOMP_FILTER=y -CONFIG_HAVE_ARCH_THREAD_STRUCT_WHITELIST=y -CONFIG_HAVE_ARCH_TRACEHOOK=y -CONFIG_HAVE_ARM_SCU=y -CONFIG_HAVE_ARM_TWD=y -CONFIG_HAVE_CLK=y -CONFIG_HAVE_CLK_PREPARE=y -CONFIG_HAVE_CONTEXT_TRACKING=y -CONFIG_HAVE_C_RECORDMCOUNT=y -CONFIG_HAVE_DEBUG_KMEMLEAK=y -CONFIG_HAVE_DMA_CONTIGUOUS=y -CONFIG_HAVE_DYNAMIC_FTRACE=y -CONFIG_HAVE_DYNAMIC_FTRACE_WITH_REGS=y -CONFIG_HAVE_EBPF_JIT=y -CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y -CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y -CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y -CONFIG_HAVE_FUNCTION_TRACER=y -CONFIG_HAVE_GENERIC_DMA_COHERENT=y -CONFIG_HAVE_IDE=y -CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y -CONFIG_HAVE_LD_DEAD_CODE_DATA_ELIMINATION=y -CONFIG_HAVE_MEMBLOCK=y -CONFIG_HAVE_MOD_ARCH_SPECIFIC=y -CONFIG_HAVE_NET_DSA=y -CONFIG_HAVE_OPROFILE=y -CONFIG_HAVE_OPTPROBES=y -CONFIG_HAVE_PERF_EVENTS=y -CONFIG_HAVE_PERF_REGS=y -CONFIG_HAVE_PERF_USER_STACK_DUMP=y -CONFIG_HAVE_PROC_CPU=y -CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y -CONFIG_HAVE_RSEQ=y -CONFIG_HAVE_SMP=y -CONFIG_HAVE_SYSCALL_TRACEPOINTS=y -CONFIG_HAVE_UID16=y -CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y -CONFIG_HWMON=y -CONFIG_HZ_FIXED=0 -CONFIG_HZ_PERIODIC=y -CONFIG_I2C=y -CONFIG_I2C_BOARDINFO=y -CONFIG_I2C_CHARDEV=y -CONFIG_I2C_CNS3XXX=y -# CONFIG_INITRAMFS_FORCE is not set -CONFIG_INITRAMFS_SOURCE="" -CONFIG_IP6_NF_FILTER=m -CONFIG_IP6_NF_IPTABLES=m -CONFIG_IP6_NF_MANGLE=m -CONFIG_IP6_NF_TARGET_REJECT=m -CONFIG_IPV6=y -CONFIG_IPV6_MROUTE=y -CONFIG_IPV6_MULTIPLE_TABLES=y -# CONFIG_IPV6_PIMSM_V2 is not set -CONFIG_IPV6_SUBTREES=y -CONFIG_IP_MROUTE_COMMON=y -CONFIG_IP_NF_FILTER=m -CONFIG_IP_NF_IPTABLES=m -CONFIG_IP_NF_MANGLE=m -CONFIG_IP_NF_NAT=m -CONFIG_IP_NF_TARGET_MASQUERADE=m -CONFIG_IP_NF_TARGET_REDIRECT=m -CONFIG_IP_NF_TARGET_REJECT=m -CONFIG_IRQCHIP=y -CONFIG_IRQ_DOMAIN=y -CONFIG_IRQ_DOMAIN_HIERARCHY=y -CONFIG_IRQ_FORCED_THREADING=y -CONFIG_IRQ_WORK=y -CONFIG_KALLSYMS=y -CONFIG_KALLSYMS_UNCOMPRESSED=y -CONFIG_LEDS_GPIO=y -# CONFIG_LEDS_TRIGGER_NETDEV is not set -CONFIG_LIBFDT=y -CONFIG_LOCK_DEBUGGING_SUPPORT=y -CONFIG_LOCK_SPIN_ON_OWNER=y -# CONFIG_MACH_CNS3420VB is not set -CONFIG_MACH_GW2388=y -CONFIG_MAGIC_SYSRQ=y -CONFIG_MDIO_BUS=y -CONFIG_MDIO_DEVICE=y -CONFIG_MEMFD_CREATE=y -CONFIG_MIGHT_HAVE_CACHE_L2X0=y -CONFIG_MIGHT_HAVE_PCI=y -CONFIG_MIGRATION=y -CONFIG_MMC=y -CONFIG_MMC_BLOCK=y -CONFIG_MMC_SDHCI=y -CONFIG_MMC_SDHCI_CNS3XXX=y -# CONFIG_MMC_SDHCI_PCI is not set -CONFIG_MMC_SDHCI_PLTFM=y -# CONFIG_MMC_TIFM_SD is not set -CONFIG_MODULES_USE_ELF_REL=y -CONFIG_MPCORE_WATCHDOG=y -CONFIG_MTD_M25P80=y -# CONFIG_MTD_OF_PARTS is not set -CONFIG_MTD_PHYSMAP=y -# CONFIG_MTD_PHYSMAP_OF is not set -CONFIG_MTD_SPI_NOR=y -CONFIG_MTD_SPLIT_FIRMWARE=y -CONFIG_MTD_SPLIT_UIMAGE_FW=y -CONFIG_MUTEX_SPIN_ON_OWNER=y -CONFIG_NEED_DMA_MAP_STATE=y -CONFIG_NETFILTER=y -CONFIG_NETFILTER_ADVANCED=y -CONFIG_NETFILTER_XTABLES=m -CONFIG_NETFILTER_XT_MARK=m -CONFIG_NETFILTER_XT_MATCH_COMMENT=m -CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m -CONFIG_NETFILTER_XT_MATCH_LIMIT=m -CONFIG_NETFILTER_XT_MATCH_MAC=m -CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m -CONFIG_NETFILTER_XT_MATCH_STATE=m -CONFIG_NETFILTER_XT_MATCH_TIME=m -CONFIG_NETFILTER_XT_NAT=m -CONFIG_NETFILTER_XT_TARGET_LOG=m -CONFIG_NETFILTER_XT_TARGET_REDIRECT=m -CONFIG_NETFILTER_XT_TARGET_TCPMSS=m -CONFIG_NET_FLOW_LIMIT=y -CONFIG_NF_CONNTRACK=m -CONFIG_NF_CONNTRACK_RTCACHE=m -CONFIG_NF_DEFRAG_IPV4=m -CONFIG_NF_DEFRAG_IPV6=m -CONFIG_NF_LOG_COMMON=m -CONFIG_NF_LOG_IPV4=m -CONFIG_NF_LOG_IPV6=m -CONFIG_NF_NAT=m -CONFIG_NF_NAT_IPV4=m -CONFIG_NF_NAT_NEEDED=y -CONFIG_NF_NAT_REDIRECT=y -CONFIG_NF_REJECT_IPV4=m -CONFIG_NF_REJECT_IPV6=m -CONFIG_NLS=y -CONFIG_NO_BOOTMEM=y -CONFIG_NR_CPUS=2 -CONFIG_NTP_PPS=y -CONFIG_NVMEM=y -CONFIG_OF=y -CONFIG_OF_ADDRESS=y -CONFIG_OF_EARLY_FLATTREE=y -CONFIG_OF_FLATTREE=y -CONFIG_OF_GPIO=y -CONFIG_OF_IRQ=y -CONFIG_OF_KOBJ=y -CONFIG_OF_MDIO=y -CONFIG_OF_NET=y -CONFIG_OF_RESERVED_MEM=y -CONFIG_OLD_SIGACTION=y -CONFIG_OLD_SIGSUSPEND3=y -CONFIG_OUTER_CACHE=y -CONFIG_OUTER_CACHE_SYNC=y -CONFIG_PADATA=y -CONFIG_PAGE_OFFSET=0xC0000000 -CONFIG_PCI=y -CONFIG_PCI_DISABLE_COMMON_QUIRKS=y -CONFIG_PCI_DOMAINS=y -CONFIG_PCI_DOMAINS_GENERIC=y -# CONFIG_PCI_V3_SEMI is not set -CONFIG_PERF_USE_VMALLOC=y -CONFIG_PGTABLE_LEVELS=2 -CONFIG_PHYLIB=y -CONFIG_PL310_ERRATA_588369=y -CONFIG_PL310_ERRATA_727915=y -CONFIG_PL310_ERRATA_753970=y -CONFIG_PL310_ERRATA_769419=y -CONFIG_PPP=m -CONFIG_PPPOE=m -CONFIG_PPP_ASYNC=m -CONFIG_PPS=y -CONFIG_PPS_CLIENT_GPIO=y -CONFIG_PRINTK_TIME=y -CONFIG_PROC_VMCORE=y -CONFIG_RAID_ATTRS=y -CONFIG_RATIONAL=y -CONFIG_RCU_NEED_SEGCBLIST=y -CONFIG_RCU_STALL_COMMON=y -CONFIG_REFCOUNT_FULL=y -CONFIG_REGMAP=y -CONFIG_REGMAP_I2C=y -CONFIG_REGMAP_SPI=y -CONFIG_RELAY=y -CONFIG_RFS_ACCEL=y -CONFIG_RPS=y -CONFIG_RTC_CLASS=y -CONFIG_RTC_DRV_DS1672=y -CONFIG_RTC_I2C_AND_SPI=y -CONFIG_RTC_MC146818_LIB=y -CONFIG_RWSEM_SPIN_ON_OWNER=y -CONFIG_RWSEM_XCHGADD_ALGORITHM=y -CONFIG_SATA_AHCI=y -CONFIG_SATA_AHCI_PLATFORM=y -CONFIG_SCSI=y -CONFIG_SENSORS_AD7418=y -CONFIG_SERIAL_8250_FSL=y -CONFIG_SERIAL_8250_NR_UARTS=3 -CONFIG_SERIAL_8250_RUNTIME_UARTS=3 -CONFIG_SG_POOL=y -CONFIG_SLHC=m -CONFIG_SMP=y -CONFIG_SMP_ON_UP=y -CONFIG_SPARSE_IRQ=y -CONFIG_SPI=y -CONFIG_SPI_BITBANG=y -CONFIG_SPI_CNS3XXX=y -CONFIG_SPI_MASTER=y -CONFIG_SPI_MEM=y -CONFIG_SRCU=y -# CONFIG_STAGING is not set -CONFIG_SWPHY=y -CONFIG_SYS_SUPPORTS_APM_EMULATION=y -CONFIG_TICK_CPU_ACCOUNTING=y -CONFIG_TIMER_OF=y -CONFIG_TIMER_PROBE=y -CONFIG_TREE_RCU=y -CONFIG_TREE_SRCU=y -CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" -CONFIG_USB=y -CONFIG_USB_ANNOUNCE_NEW_DEVICES=y -CONFIG_USB_CNS3XXX_EHCI=y -CONFIG_USB_CNS3XXX_OHCI=y -CONFIG_USB_COMMON=y -CONFIG_USB_DWC2=y -CONFIG_USB_DWC2_HOST=y -# CONFIG_USB_DWC2_TRACK_MISSED_SOFS is not set -CONFIG_USB_EHCI_HCD=y -CONFIG_USB_EHCI_HCD_PLATFORM=y -CONFIG_USB_GADGET=y -CONFIG_USB_OHCI_HCD=y -CONFIG_USB_OHCI_HCD_PLATFORM=y -# CONFIG_USB_ROLE_SWITCH is not set -CONFIG_USB_SUPPORT=y -CONFIG_USE_OF=y -CONFIG_VFP=y -CONFIG_WATCHDOG_CORE=y -CONFIG_WATCHDOG_NOWAYOUT=y -CONFIG_XPS=y -CONFIG_XZ_DEC_ARM=y -CONFIG_XZ_DEC_BCJ=y -CONFIG_ZBOOT_ROM_BSS=0 -CONFIG_ZBOOT_ROM_TEXT=0 diff --git a/target/linux/cns3xxx/files/arch/arm/mach-cns3xxx/cns3xxx_fiq.S b/target/linux/cns3xxx/files/arch/arm/mach-cns3xxx/cns3xxx_fiq.S deleted file mode 100644 index b1155ef570..0000000000 --- a/target/linux/cns3xxx/files/arch/arm/mach-cns3xxx/cns3xxx_fiq.S +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2012 Gateworks Corporation - * Chris Lang - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include - -#define D_CACHE_LINE_SIZE 32 - - .text - -/* - * R8 - DMA Start Address - * R9 - DMA Length - * R10 - DMA Direction - * R11 - DMA type - * R12 - fiq_buffer Address -*/ - - .global cns3xxx_fiq_end -ENTRY(cns3xxx_fiq_start) - str r8, [r13] - - ldmia r12, {r8, r9, r10} - and r11, r10, #0x3000000 - and r10, r10, #0xff - - teq r11, #0x1000000 - beq cns3xxx_dma_map_area - teq r11, #0x2000000 - beq cns3xxx_dma_unmap_area - /* fall through */ -cns3xxx_dma_flush_range: - bic r8, r8, #D_CACHE_LINE_SIZE - 1 -1: - mcr p15, 0, r8, c7, c14, 1 @ clean & invalidate D line - add r8, r8, #D_CACHE_LINE_SIZE - cmp r8, r9 - blo 1b - /* fall through */ -cns3xxx_fiq_exit: - mov r8, #0 - str r8, [r12, #8] - mcr p15, 0, r8, c7, c10, 4 @ drain write buffer - subs pc, lr, #4 - -cns3xxx_dma_map_area: - add r9, r9, r8 - teq r10, #DMA_FROM_DEVICE - beq cns3xxx_dma_inv_range - teq r10, #DMA_TO_DEVICE - bne cns3xxx_dma_flush_range - /* fall through */ -cns3xxx_dma_clean_range: - bic r8, r8, #D_CACHE_LINE_SIZE - 1 -1: - mcr p15, 0, r8, c7, c10, 1 @ clean D line - add r8, r8, #D_CACHE_LINE_SIZE - cmp r8, r9 - blo 1b - b cns3xxx_fiq_exit - -cns3xxx_dma_unmap_area: - add r9, r9, r8 - teq r10, #DMA_TO_DEVICE - beq cns3xxx_fiq_exit - /* fall through */ -cns3xxx_dma_inv_range: - tst r8, #D_CACHE_LINE_SIZE - 1 - bic r8, r8, #D_CACHE_LINE_SIZE - 1 - mcrne p15, 0, r8, c7, c10, 1 @ clean D line - tst r9, #D_CACHE_LINE_SIZE - 1 - bic r9, r9, #D_CACHE_LINE_SIZE - 1 - mcrne p15, 0, r9, c7, c14, 1 @ clean & invalidate D line -1: - mcr p15, 0, r8, c7, c6, 1 @ invalidate D line - add r8, r8, #D_CACHE_LINE_SIZE - cmp r8, r9 - blo 1b - b cns3xxx_fiq_exit - -cns3xxx_fiq_end: diff --git a/target/linux/cns3xxx/files/arch/arm/mach-cns3xxx/gpio.c b/target/linux/cns3xxx/files/arch/arm/mach-cns3xxx/gpio.c deleted file mode 100644 index bdf930a79e..0000000000 --- a/target/linux/cns3xxx/files/arch/arm/mach-cns3xxx/gpio.c +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Copyright 2012 Gateworks Corporation - * Chris Lang - * Tim Harvey - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, Version 2, as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/* - * Registers - */ -#define GPIO_INPUT 0x04 -#define GPIO_DIR 0x08 -#define GPIO_SET 0x10 -#define GPIO_CLEAR 0x14 -#define GPIO_INTERRUPT_ENABLE 0x20 -#define GPIO_INTERRUPT_RAW_STATUS 0x24 -#define GPIO_INTERRUPT_MASKED_STATUS 0x28 -#define GPIO_INTERRUPT_MASK 0x2C -#define GPIO_INTERRUPT_CLEAR 0x30 -#define GPIO_INTERRUPT_TRIGGER_METHOD 0x34 -#define GPIO_INTERRUPT_TRIGGER_BOTH_EDGES 0x38 -#define GPIO_INTERRUPT_TRIGGER_TYPE 0x3C - -#define GPIO_INTERRUPT_TRIGGER_METHOD_EDGE 0 -#define GPIO_INTERRUPT_TRIGGER_METHOD_LEVEL 1 -#define GPIO_INTERRUPT_TRIGGER_EDGE_SINGLE 0 -#define GPIO_INTERRUPT_TRIGGER_EDGE_BOTH 1 -#define GPIO_INTERRUPT_TRIGGER_TYPE_RISING 0 -#define GPIO_INTERRUPT_TRIGGER_TYPE_FALLING 1 -#define GPIO_INTERRUPT_TRIGGER_TYPE_HIGH 0 -#define GPIO_INTERRUPT_TRIGGER_TYPE_LOW 1 - -struct cns3xxx_gpio_chip { - struct gpio_chip chip; - struct irq_domain *domain; - spinlock_t lock; - void __iomem *base; -}; - -static struct cns3xxx_gpio_chip cns3xxx_gpio_chips[2]; -static int cns3xxx_gpio_chip_count; - -static inline void -__set_direction(struct cns3xxx_gpio_chip *cchip, unsigned pin, int input) -{ - u32 reg; - - reg = __raw_readl(cchip->base + GPIO_DIR); - if (input) - reg &= ~(1 << pin); - else - reg |= (1 << pin); - __raw_writel(reg, cchip->base + GPIO_DIR); -} - -/* - * GENERIC_GPIO primatives - */ -static int cns3xxx_gpio_direction_input(struct gpio_chip *chip, unsigned pin) -{ - struct cns3xxx_gpio_chip *cchip = - container_of(chip, struct cns3xxx_gpio_chip, chip); - unsigned long flags; - - spin_lock_irqsave(&cchip->lock, flags); - __set_direction(cchip, pin, 1); - spin_unlock_irqrestore(&cchip->lock, flags); - - return 0; -} - -static int cns3xxx_gpio_get(struct gpio_chip *chip, unsigned pin) -{ - struct cns3xxx_gpio_chip *cchip = - container_of(chip, struct cns3xxx_gpio_chip, chip); - int val; - - val = ((__raw_readl(cchip->base + GPIO_INPUT) >> pin) & 0x1); - - return val; -} - -static int cns3xxx_gpio_direction_output(struct gpio_chip *chip, unsigned pin, int level) -{ - struct cns3xxx_gpio_chip *cchip = - container_of(chip, struct cns3xxx_gpio_chip, chip); - unsigned long flags; - - spin_lock_irqsave(&cchip->lock, flags); - if (level) - __raw_writel(1 << pin, cchip->base + GPIO_SET); - else - __raw_writel(1 << pin, cchip->base + GPIO_CLEAR); - __set_direction(cchip, pin, 0); - spin_unlock_irqrestore(&cchip->lock, flags); - - return 0; -} - -static void cns3xxx_gpio_set(struct gpio_chip *chip, unsigned pin, - int level) -{ - struct cns3xxx_gpio_chip *cchip = - container_of(chip, struct cns3xxx_gpio_chip, chip); - - if (level) - __raw_writel(1 << pin, cchip->base + GPIO_SET); - else - __raw_writel(1 << pin, cchip->base + GPIO_CLEAR); -} - -static int cns3xxx_gpio_to_irq(struct gpio_chip *chip, unsigned pin) -{ - struct cns3xxx_gpio_chip *cchip = - container_of(chip, struct cns3xxx_gpio_chip, chip); - - return irq_find_mapping(cchip->domain, pin); -} - - -/* - * IRQ support - */ - -/* one interrupt per GPIO controller (GPIOA/GPIOB) - * this is called in task context, with IRQs enabled - */ -static void cns3xxx_gpio_irq_handler(struct irq_desc *desc) -{ - struct cns3xxx_gpio_chip *cchip = irq_desc_get_handler_data(desc); - struct irq_chip *chip = irq_desc_get_chip(desc); - u16 i; - u32 reg; - - chained_irq_enter(chip, desc); /* mask and ack the base interrupt */ - - /* see which pin(s) triggered the interrupt */ - reg = __raw_readl(cchip->base + GPIO_INTERRUPT_RAW_STATUS); - for (i = 0; i < 32; i++) { - if (reg & (1 << i)) { - /* let the generic IRQ layer handle an interrupt */ - generic_handle_irq(irq_find_mapping(cchip->domain, i)); - } - } - - chained_irq_exit(chip, desc); /* unmask the base interrupt */ -} - -static int cns3xxx_gpio_irq_set_type(struct irq_data *d, u32 irqtype) -{ - struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); - struct cns3xxx_gpio_chip *cchip = gc->private; - u32 gpio = d->hwirq; - unsigned long flags; - u32 method, edges, type; - - spin_lock_irqsave(&cchip->lock, flags); - method = __raw_readl(cchip->base + GPIO_INTERRUPT_TRIGGER_METHOD); - edges = __raw_readl(cchip->base + GPIO_INTERRUPT_TRIGGER_BOTH_EDGES); - type = __raw_readl(cchip->base + GPIO_INTERRUPT_TRIGGER_TYPE); - method &= ~(1 << gpio); - edges &= ~(1 << gpio); - type &= ~(1 << gpio); - - switch(irqtype) { - case IRQ_TYPE_EDGE_RISING: - method |= (GPIO_INTERRUPT_TRIGGER_METHOD_EDGE << gpio); - edges |= (GPIO_INTERRUPT_TRIGGER_EDGE_SINGLE << gpio); - type |= (GPIO_INTERRUPT_TRIGGER_TYPE_RISING << gpio); - break; - case IRQ_TYPE_EDGE_FALLING: - method |= (GPIO_INTERRUPT_TRIGGER_METHOD_EDGE << gpio); - edges |= (GPIO_INTERRUPT_TRIGGER_EDGE_SINGLE << gpio); - type |= (GPIO_INTERRUPT_TRIGGER_TYPE_FALLING << gpio); - break; - case IRQ_TYPE_EDGE_BOTH: - method |= (GPIO_INTERRUPT_TRIGGER_METHOD_EDGE << gpio); - edges |= (GPIO_INTERRUPT_TRIGGER_EDGE_BOTH << gpio); - break; - case IRQ_TYPE_LEVEL_LOW: - method |= (GPIO_INTERRUPT_TRIGGER_METHOD_LEVEL << gpio); - type |= (GPIO_INTERRUPT_TRIGGER_TYPE_LOW << gpio); - break; - case IRQ_TYPE_LEVEL_HIGH: - method |= (GPIO_INTERRUPT_TRIGGER_METHOD_LEVEL << gpio); - type |= (GPIO_INTERRUPT_TRIGGER_TYPE_HIGH << gpio); - break; - default: - printk(KERN_WARNING "No irq type\n"); - spin_unlock_irqrestore(&cchip->lock, flags); - return -EINVAL; - } - - __raw_writel(method, cchip->base + GPIO_INTERRUPT_TRIGGER_METHOD); - __raw_writel(edges, cchip->base + GPIO_INTERRUPT_TRIGGER_BOTH_EDGES); - __raw_writel(type, cchip->base + GPIO_INTERRUPT_TRIGGER_TYPE); - spin_unlock_irqrestore(&cchip->lock, flags); - - if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) - irq_set_handler_locked(d, handle_level_irq); - else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) - irq_set_handler_locked(d, handle_edge_irq); - - return 0; -} - -void __init cns3xxx_gpio_init(int gpio_base, int ngpio, - u32 base, int irq, int secondary_irq_base) -{ - struct cns3xxx_gpio_chip *cchip; - struct irq_chip_generic *gc; - struct irq_chip_type *ct; - char gc_label[16]; - int irq_base; - - if (cns3xxx_gpio_chip_count == ARRAY_SIZE(cns3xxx_gpio_chips)) - return; - - snprintf(gc_label, sizeof(gc_label), "cns3xxx_gpio%d", - cns3xxx_gpio_chip_count); - - cchip = cns3xxx_gpio_chips + cns3xxx_gpio_chip_count; - cchip->chip.label = kstrdup(gc_label, GFP_KERNEL); - cchip->chip.direction_input = cns3xxx_gpio_direction_input; - cchip->chip.get = cns3xxx_gpio_get; - cchip->chip.direction_output = cns3xxx_gpio_direction_output; - cchip->chip.set = cns3xxx_gpio_set; - cchip->chip.to_irq = cns3xxx_gpio_to_irq; - cchip->chip.base = gpio_base; - cchip->chip.ngpio = ngpio; - cchip->chip.can_sleep = 0; - spin_lock_init(&cchip->lock); - cchip->base = (void __iomem *)base; - - BUG_ON(gpiochip_add(&cchip->chip) < 0); - cns3xxx_gpio_chip_count++; - - /* clear GPIO interrupts */ - __raw_writel(0xffff, cchip->base + GPIO_INTERRUPT_CLEAR); - - irq_base = irq_alloc_descs(-1, secondary_irq_base, ngpio, - numa_node_id()); - if (irq_base < 0) - goto out_irqdesc_free; - - cchip->domain = irq_domain_add_legacy(NULL, ngpio, irq_base, 0, - &irq_domain_simple_ops, NULL); - if (!cchip->domain) - goto out_irqdesc_free; - - /* - * IRQ chip init - */ - gc = irq_alloc_generic_chip("cns3xxx_gpio_irq", 1, irq_base, - cchip->base, handle_edge_irq); - - gc->private = cchip; - - ct = gc->chip_types; - ct->type = IRQ_TYPE_EDGE_FALLING; - ct->regs.ack = GPIO_INTERRUPT_CLEAR; - ct->chip.irq_ack = irq_gc_ack_set_bit; - ct->regs.mask = GPIO_INTERRUPT_ENABLE; - ct->chip.irq_enable = irq_gc_mask_set_bit; - ct->chip.irq_disable = irq_gc_mask_clr_bit; - ct->chip.irq_set_type = cns3xxx_gpio_irq_set_type; - ct->handler = handle_edge_irq; - - irq_setup_generic_chip(gc, IRQ_MSK(ngpio), IRQ_GC_INIT_MASK_CACHE, - IRQ_NOREQUEST, 0); - irq_set_chained_handler(irq, cns3xxx_gpio_irq_handler); - irq_set_handler_data(irq, cchip); - - return; - -out_irqdesc_free: - irq_free_descs(irq_base, ngpio); -} diff --git a/target/linux/cns3xxx/files/arch/arm/mach-cns3xxx/headsmp.S b/target/linux/cns3xxx/files/arch/arm/mach-cns3xxx/headsmp.S deleted file mode 100644 index f0da8ec0a5..0000000000 --- a/target/linux/cns3xxx/files/arch/arm/mach-cns3xxx/headsmp.S +++ /dev/null @@ -1,41 +0,0 @@ -/* - * linux/arch/arm/mach-cns3xxx/headsmp.S - * - * Cloned from linux/arch/arm/plat-versatile/headsmp.S - * - * Copyright (c) 2003 ARM Limited - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include - -/* - * CNS3XXX specific entry point for secondary CPUs. This provides - * a "holding pen" into which all secondary cores are held until we're - * ready for them to initialise. - */ -ENTRY(cns3xxx_secondary_startup) - mrc p15, 0, r0, c0, c0, 5 - and r0, r0, #15 - adr r4, 1f - ldmia r4, {r5, r6} - sub r4, r4, r5 - add r6, r6, r4 -pen: ldr r7, [r6] - cmp r7, r0 - bne pen - - /* - * we've been released from the holding pen: secondary_stack - * should now contain the SVC stack for this core - */ - b secondary_startup -ENDPROC(cns3xxx_secondary_startup) - - .align 2 -1: .long . - .long pen_release diff --git a/target/linux/cns3xxx/files/arch/arm/mach-cns3xxx/hotplug.c b/target/linux/cns3xxx/files/arch/arm/mach-cns3xxx/hotplug.c deleted file mode 100644 index be0d499a38..0000000000 --- a/target/linux/cns3xxx/files/arch/arm/mach-cns3xxx/hotplug.c +++ /dev/null @@ -1,130 +0,0 @@ -/* linux arch/arm/mach-cns3xxx/hotplug.c - * - * Cloned from linux/arch/arm/mach-realview/hotplug.c - * - * Copyright (C) 2002 ARM Ltd. - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ - -#include -#include -#include - -#include - -extern volatile int pen_release; - -static inline void cpu_enter_lowpower(void) -{ - unsigned int v; - - flush_cache_all(); - asm volatile( - " mcr p15, 0, %1, c7, c5, 0\n" - " mcr p15, 0, %1, c7, c10, 4\n" - /* - * Turn off coherency - */ - " mrc p15, 0, %0, c1, c0, 1\n" - " bic %0, %0, %3\n" - " mcr p15, 0, %0, c1, c0, 1\n" - " mrc p15, 0, %0, c1, c0, 0\n" - " bic %0, %0, %2\n" - " mcr p15, 0, %0, c1, c0, 0\n" - : "=&r" (v) - : "r" (0), "Ir" (CR_C), "Ir" (0x40) - : "cc"); -} - -static inline void cpu_leave_lowpower(void) -{ - unsigned int v; - - asm volatile( - "mrc p15, 0, %0, c1, c0, 0\n" - " orr %0, %0, %1\n" - " mcr p15, 0, %0, c1, c0, 0\n" - " mrc p15, 0, %0, c1, c0, 1\n" - " orr %0, %0, %2\n" - " mcr p15, 0, %0, c1, c0, 1\n" - : "=&r" (v) - : "Ir" (CR_C), "Ir" (0x40) - : "cc"); -} - -static inline void platform_do_lowpower(unsigned int cpu, int *spurious) -{ - /* - * there is no power-control hardware on this platform, so all - * we can do is put the core into WFI; this is safe as the calling - * code will have already disabled interrupts - */ - for (;;) { - /* - * here's the WFI - */ - asm(".word 0xe320f003\n" - : - : - : "memory", "cc"); - - if (pen_release == cpu) { - /* - * OK, proper wakeup, we're done - */ - break; - } - - /* - * Getting here, means that we have come out of WFI without - * having been woken up - this shouldn't happen - * - * Just note it happening - when we're woken, we can report - * its occurrence. - */ - (*spurious)++; - } -} - -int platform_cpu_kill(unsigned int cpu) -{ - return 1; -} - -/* - * platform-specific code to shutdown a CPU - * - * Called with IRQs disabled - */ -void platform_cpu_die(unsigned int cpu) -{ - int spurious = 0; - - /* - * we're ready for shutdown now, so do it - */ - cpu_enter_lowpower(); - platform_do_lowpower(cpu, &spurious); - - /* - * bring this CPU back into the world of cache - * coherency, and then restore interrupts - */ - cpu_leave_lowpower(); - - if (spurious) - pr_warn("CPU%u: %u spurious wakeup calls\n", cpu, spurious); -} - -int platform_cpu_disable(unsigned int cpu) -{ - /* - * we don't allow CPU 0 to be shutdown (it is still too special - * e.g. clock tick interrupts) - */ - return cpu == 0 ? -EPERM : 0; -} diff --git a/target/linux/cns3xxx/files/arch/arm/mach-cns3xxx/include/mach/gpio.h b/target/linux/cns3xxx/files/arch/arm/mach-cns3xxx/include/mach/gpio.h deleted file mode 100644 index 8c66748ac9..0000000000 --- a/target/linux/cns3xxx/files/arch/arm/mach-cns3xxx/include/mach/gpio.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * arch/arm/mach-cns3xxx/include/mach/gpio.h - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - * - */ -#ifndef __ASM_ARCH_CNS3XXX_GPIO_H -#define __ASM_ARCH_CNS3XXX_GPIO_H - -#include - -extern void __init cns3xxx_gpio_init(int gpio_base, int ngpio, - u32 base, int irq, int secondary_irq_base); - -#endif diff --git a/target/linux/cns3xxx/files/arch/arm/mach-cns3xxx/include/mach/smp.h b/target/linux/cns3xxx/files/arch/arm/mach-cns3xxx/include/mach/smp.h deleted file mode 100644 index e5bc56823e..0000000000 --- a/target/linux/cns3xxx/files/arch/arm/mach-cns3xxx/include/mach/smp.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef __MACH_SMP_H -#define __MACH_SMP_H - -extern void smp_dma_map_area(const void *, size_t, int); -extern void smp_dma_unmap_area(const void *, size_t, int); -extern void smp_dma_flush_range(const void *, const void *); - -#endif diff --git a/target/linux/cns3xxx/files/arch/arm/mach-cns3xxx/laguna.c b/target/linux/cns3xxx/files/arch/arm/mach-cns3xxx/laguna.c deleted file mode 100644 index bee7348266..0000000000 --- a/target/linux/cns3xxx/files/arch/arm/mach-cns3xxx/laguna.c +++ /dev/null @@ -1,1123 +0,0 @@ -/* - * Gateworks Corporation Laguna Platform - * - * Copyright 2000 Deep Blue Solutions Ltd - * Copyright 2008 ARM Limited - * Copyright 2008 Cavium Networks - * Scott Shu - * Copyright 2010 MontaVista Software, LLC. - * Anton Vorontsov - * Copyright 2011 Gateworks Corporation - * Chris Lang - * Copyright 2012-2013 Gateworks Corporation - * Tim Harvey - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, Version 2, as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "core.h" -#include "devices.h" -#include "cns3xxx.h" -#include "pm.h" - -#define ARRAY_AND_SIZE(x) (x), ARRAY_SIZE(x) - -// Config 1 Bitmap -#define ETH0_LOAD BIT(0) -#define ETH1_LOAD BIT(1) -#define ETH2_LOAD BIT(2) -#define SATA0_LOAD BIT(3) -#define SATA1_LOAD BIT(4) -#define PCM_LOAD BIT(5) -#define I2S_LOAD BIT(6) -#define SPI0_LOAD BIT(7) -#define SPI1_LOAD BIT(8) -#define PCIE0_LOAD BIT(9) -#define PCIE1_LOAD BIT(10) -#define USB0_LOAD BIT(11) -#define USB1_LOAD BIT(12) -#define USB1_ROUTE BIT(13) -#define SD_LOAD BIT(14) -#define UART0_LOAD BIT(15) -#define UART1_LOAD BIT(16) -#define UART2_LOAD BIT(17) -#define MPCI0_LOAD BIT(18) -#define MPCI1_LOAD BIT(19) -#define MPCI2_LOAD BIT(20) -#define MPCI3_LOAD BIT(21) -#define FP_BUT_LOAD BIT(22) -#define FP_BUT_HEADER_LOAD BIT(23) -#define FP_LED_LOAD BIT(24) -#define FP_LED_HEADER_LOAD BIT(25) -#define FP_TAMPER_LOAD BIT(26) -#define HEADER_33V_LOAD BIT(27) -#define SATA_POWER_LOAD BIT(28) -#define FP_POWER_LOAD BIT(29) -#define GPIO_HEADER_LOAD BIT(30) -#define GSP_BAT_LOAD BIT(31) - -// Config 2 Bitmap -#define FAN_LOAD BIT(0) -#define SPI_FLASH_LOAD BIT(1) -#define NOR_FLASH_LOAD BIT(2) -#define GPS_LOAD BIT(3) -#define SUPPLY_5V_LOAD BIT(6) -#define SUPPLY_33V_LOAD BIT(7) - -struct laguna_board_info { - char model[16]; - u32 config_bitmap; - u32 config2_bitmap; - u8 nor_flash_size; - u8 spi_flash_size; -}; - -static struct laguna_board_info laguna_info __initdata; - -/* - * NOR Flash - */ -static struct mtd_partition laguna_nor_partitions[] = { - { - .name = "uboot", - .offset = 0, - .size = SZ_256K, - .mask_flags = MTD_WRITEABLE, - }, { - .name = "params", - .offset = MTDPART_OFS_APPEND, - .size = SZ_128K, - }, { - .name = "firmware", - .offset = MTDPART_OFS_APPEND, - .size = MTDPART_SIZ_FULL, - }, -}; - -static struct physmap_flash_data laguna_nor_pdata = { - .width = 2, - .parts = laguna_nor_partitions, - .nr_parts = ARRAY_SIZE(laguna_nor_partitions), -}; - -static struct resource laguna_nor_res = { - .start = CNS3XXX_FLASH_BASE, - .end = CNS3XXX_FLASH_BASE + SZ_128M - 1, - .flags = IORESOURCE_MEM | IORESOURCE_MEM_32BIT, -}; - -static struct platform_device laguna_nor_pdev = { - .name = "physmap-flash", - .id = 0, - .resource = &laguna_nor_res, - .num_resources = 1, - .dev = { - .platform_data = &laguna_nor_pdata, - }, -}; - -/* - * SPI - */ -static struct mtd_partition laguna_spi_partitions[] = { - { - .name = "uboot", - .offset = 0, - .size = SZ_256K, - .mask_flags = MTD_WRITEABLE, - }, { - .name = "params", - .offset = MTDPART_OFS_APPEND, - .size = SZ_256K, - }, { - .name = "firmware", - .offset = MTDPART_OFS_APPEND, - .size = MTDPART_SIZ_FULL, - }, -}; - -static struct flash_platform_data laguna_spi_pdata = { - .parts = laguna_spi_partitions, - .nr_parts = ARRAY_SIZE(laguna_spi_partitions), -}; - -static struct spi_board_info __initdata laguna_spi_devices[] = { - { - .modalias = "m25p80", - .platform_data = &laguna_spi_pdata, - .max_speed_hz = 50000000, - .bus_num = 1, - .chip_select = 0, - }, -}; - -static struct resource laguna_spi_resource = { - .start = CNS3XXX_SSP_BASE + 0x40, - .end = CNS3XXX_SSP_BASE + 0x6f, - .flags = IORESOURCE_MEM, -}; - -static struct platform_device laguna_spi_controller = { - .name = "cns3xxx_spi", - .resource = &laguna_spi_resource, - .num_resources = 1, -}; - -/* - * LED's - */ -static struct gpio_led laguna_gpio_leds[] = { - { - .name = "user1", /* Green Led */ - .gpio = 115, - .active_low = 1, - },{ - .name = "user2", /* Red Led */ - .gpio = 114, - .active_low = 1, - },{ - .name = "pwr1", /* Green Led */ - .gpio = 116, - .active_low = 1, - },{ - .name = "pwr2", /* Yellow Led */ - .gpio = 117, - .active_low = 1, - },{ - .name = "txd1", /* Green Led */ - .gpio = 118, - .active_low = 1, - },{ - .name = "txd2", /* Yellow Led */ - .gpio = 119, - .active_low = 1, - },{ - .name = "rxd1", /* Green Led */ - .gpio = 120, - .active_low = 1, - },{ - .name = "rxd2", /* Yellow Led */ - .gpio = 121, - .active_low = 1, - },{ - .name = "ser1", /* Green Led */ - .gpio = 122, - .active_low = 1, - },{ - .name = "ser2", /* Yellow Led */ - .gpio = 123, - .active_low = 1, - },{ - .name = "enet1", /* Green Led */ - .gpio = 124, - .active_low = 1, - },{ - .name = "enet2", /* Yellow Led */ - .gpio = 125, - .active_low = 1, - },{ - .name = "sig1_1", /* Green Led */ - .gpio = 126, - .active_low = 1, - },{ - .name = "sig1_2", /* Yellow Led */ - .gpio = 127, - .active_low = 1, - },{ - .name = "sig2_1", /* Green Led */ - .gpio = 128, - .active_low = 1, - },{ - .name = "sig2_2", /* Yellow Led */ - .gpio = 129, - .active_low = 1, - },{ - .name = "sig3_1", /* Green Led */ - .gpio = 130, - .active_low = 1, - },{ - .name = "sig3_2", /* Yellow Led */ - .gpio = 131, - .active_low = 1, - },{ - .name = "net1", /*Green Led */ - .gpio = 109, - .active_low = 1, - },{ - .name = "net2", /* Red Led */ - .gpio = 110, - .active_low = 1, - },{ - .name = "mod1", /* Green Led */ - .gpio = 111, - .active_low = 1, - },{ - .name = "mod2", /* Red Led */ - .gpio = 112, - .active_low = 1, - }, -}; - -static struct gpio_led_platform_data laguna_gpio_leds_data = { - .num_leds = 22, - .leds = laguna_gpio_leds, -}; - -static struct platform_device laguna_gpio_leds_device = { - .name = "leds-gpio", - .id = PLATFORM_DEVID_NONE, - .dev.platform_data = &laguna_gpio_leds_data, -}; - -/* - * Ethernet - */ -static struct cns3xxx_plat_info laguna_net_data = { - .ports = 0, - .phy = { - 0, - 1, - 2, - }, -}; - -static struct resource laguna_net_resource[] = { - { - .name = "eth0_mem", - .start = CNS3XXX_SWITCH_BASE, - .end = CNS3XXX_SWITCH_BASE + SZ_4K - 1, - .flags = IORESOURCE_MEM - }, { - .name = "eth_rx", - .start = IRQ_CNS3XXX_SW_R0RXC, - .end = IRQ_CNS3XXX_SW_R0RXC, - .flags = IORESOURCE_IRQ - }, { - .name = "eth_stat", - .start = IRQ_CNS3XXX_SW_STATUS, - .end = IRQ_CNS3XXX_SW_STATUS, - .flags = IORESOURCE_IRQ - } -}; - -static u64 laguna_net_dmamask = DMA_BIT_MASK(32); -static struct platform_device laguna_net_device = { - .name = "cns3xxx_eth", - .id = 0, - .resource = laguna_net_resource, - .num_resources = ARRAY_SIZE(laguna_net_resource), - .dev = { - .dma_mask = &laguna_net_dmamask, - .coherent_dma_mask = DMA_BIT_MASK(32), - .platform_data = &laguna_net_data, - } -}; - -/* - * UART - */ -static void __init laguna_early_serial_setup(void) -{ -#ifdef CONFIG_SERIAL_8250_CONSOLE - static struct uart_port laguna_serial_port = { - .membase = (void __iomem *)CNS3XXX_UART0_BASE_VIRT, - .mapbase = CNS3XXX_UART0_BASE, - .irq = IRQ_CNS3XXX_UART0, - .iotype = UPIO_MEM, - .flags = UPF_BOOT_AUTOCONF | UPF_FIXED_TYPE, - .regshift = 2, - .uartclk = 24000000, - .line = 0, - .type = PORT_16550A, - .fifosize = 16, - }; - - early_serial_setup(&laguna_serial_port); -#endif -} - -static struct resource laguna_uart_resources[] = { - { - .start = CNS3XXX_UART0_BASE, - .end = CNS3XXX_UART0_BASE + SZ_4K - 1, - .flags = IORESOURCE_MEM - },{ - .start = CNS3XXX_UART1_BASE, - .end = CNS3XXX_UART1_BASE + SZ_4K - 1, - .flags = IORESOURCE_MEM - },{ - .start = CNS3XXX_UART2_BASE, - .end = CNS3XXX_UART2_BASE + SZ_4K - 1, - .flags = IORESOURCE_MEM - }, -}; - -static struct plat_serial8250_port laguna_uart_data[] = { - { - .mapbase = (CNS3XXX_UART0_BASE), - .irq = IRQ_CNS3XXX_UART0, - .iotype = UPIO_MEM, - .flags = UPF_BOOT_AUTOCONF | UPF_FIXED_TYPE | UPF_IOREMAP, - .regshift = 2, - .uartclk = 24000000, - .type = PORT_16550A, - },{ - .mapbase = (CNS3XXX_UART1_BASE), - .irq = IRQ_CNS3XXX_UART1, - .iotype = UPIO_MEM, - .flags = UPF_BOOT_AUTOCONF | UPF_FIXED_TYPE | UPF_IOREMAP, - .regshift = 2, - .uartclk = 24000000, - .type = PORT_16550A, - },{ - .mapbase = (CNS3XXX_UART2_BASE), - .irq = IRQ_CNS3XXX_UART2, - .iotype = UPIO_MEM, - .flags = UPF_BOOT_AUTOCONF | UPF_FIXED_TYPE | UPF_IOREMAP, - .regshift = 2, - .uartclk = 24000000, - .type = PORT_16550A, - }, - { }, -}; - -static struct platform_device laguna_uart = { - .name = "serial8250", - .id = PLAT8250_DEV_PLATFORM, - .dev.platform_data = laguna_uart_data, - .num_resources = 3, - .resource = laguna_uart_resources -}; - -/* - * USB - */ -static struct resource cns3xxx_usb_ehci_resources[] = { - [0] = { - .start = CNS3XXX_USB_BASE, - .end = CNS3XXX_USB_BASE + SZ_16M - 1, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = IRQ_CNS3XXX_USB_EHCI, - .flags = IORESOURCE_IRQ, - }, -}; - -static u64 cns3xxx_usb_ehci_dma_mask = DMA_BIT_MASK(32); - -static int csn3xxx_usb_power_on(struct platform_device *pdev) -{ - /* - * EHCI and OHCI share the same clock and power, - * resetting twice would cause the 1st controller been reset. - * Therefore only do power up at the first up device, and - * power down at the last down device. - * - * Set USB AHB INCR length to 16 - */ - if (atomic_inc_return(&usb_pwr_ref) == 1) { - cns3xxx_pwr_power_up(1 << PM_PLL_HM_PD_CTRL_REG_OFFSET_PLL_USB); - cns3xxx_pwr_clk_en(1 << PM_CLK_GATE_REG_OFFSET_USB_HOST); - cns3xxx_pwr_soft_rst(1 << PM_SOFT_RST_REG_OFFST_USB_HOST); - __raw_writel((__raw_readl(MISC_CHIP_CONFIG_REG) | (0X2 << 24)), - MISC_CHIP_CONFIG_REG); - } - - return 0; -} - -static void csn3xxx_usb_power_off(struct platform_device *pdev) -{ - /* - * EHCI and OHCI share the same clock and power, - * resetting twice would cause the 1st controller been reset. - * Therefore only do power up at the first up device, and - * power down at the last down device. - */ - if (atomic_dec_return(&usb_pwr_ref) == 0) - cns3xxx_pwr_clk_dis(1 << PM_CLK_GATE_REG_OFFSET_USB_HOST); -} - -static struct usb_ehci_pdata cns3xxx_usb_ehci_pdata = { - .power_on = csn3xxx_usb_power_on, - .power_off = csn3xxx_usb_power_off, -}; - -static struct platform_device cns3xxx_usb_ehci_device = { - .name = "ehci-platform", - .num_resources = ARRAY_SIZE(cns3xxx_usb_ehci_resources), - .resource = cns3xxx_usb_ehci_resources, - .dev = { - .dma_mask = &cns3xxx_usb_ehci_dma_mask, - .coherent_dma_mask = DMA_BIT_MASK(32), - .platform_data = &cns3xxx_usb_ehci_pdata, - }, -}; - -static struct resource cns3xxx_usb_ohci_resources[] = { - [0] = { - .start = CNS3XXX_USB_OHCI_BASE, - .end = CNS3XXX_USB_OHCI_BASE + SZ_16M - 1, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = IRQ_CNS3XXX_USB_OHCI, - .flags = IORESOURCE_IRQ, - }, -}; - -static u64 cns3xxx_usb_ohci_dma_mask = DMA_BIT_MASK(32); - -static struct usb_ohci_pdata cns3xxx_usb_ohci_pdata = { - .num_ports = 1, - .power_on = csn3xxx_usb_power_on, - .power_off = csn3xxx_usb_power_off, -}; - -static struct platform_device cns3xxx_usb_ohci_device = { - .name = "ohci-platform", - .num_resources = ARRAY_SIZE(cns3xxx_usb_ohci_resources), - .resource = cns3xxx_usb_ohci_resources, - .dev = { - .dma_mask = &cns3xxx_usb_ohci_dma_mask, - .coherent_dma_mask = DMA_BIT_MASK(32), - .platform_data = &cns3xxx_usb_ohci_pdata, - }, -}; - -static struct resource cns3xxx_usb_otg_resources[] = { - [0] = { - .start = CNS3XXX_USBOTG_BASE, - .end = CNS3XXX_USBOTG_BASE + SZ_16M - 1, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = IRQ_CNS3XXX_USB_OTG, - .flags = IORESOURCE_IRQ, - }, -}; - -static u64 cns3xxx_usb_otg_dma_mask = DMA_BIT_MASK(32); - -static struct platform_device cns3xxx_usb_otg_device = { - .name = "dwc2", - .num_resources = ARRAY_SIZE(cns3xxx_usb_otg_resources), - .resource = cns3xxx_usb_otg_resources, - .dev = { - .dma_mask = &cns3xxx_usb_otg_dma_mask, - .coherent_dma_mask = DMA_BIT_MASK(32), - }, -}; - -/* - * I2C - */ -static struct resource laguna_i2c_resource[] = { - { - .start = CNS3XXX_SSP_BASE + 0x20, - .end = CNS3XXX_SSP_BASE + 0x3f, - .flags = IORESOURCE_MEM, - },{ - .start = IRQ_CNS3XXX_I2C, - .flags = IORESOURCE_IRQ, - }, -}; - -static struct platform_device laguna_i2c_controller = { - .name = "cns3xxx-i2c", - .num_resources = 2, - .resource = laguna_i2c_resource, -}; - -static struct nvmem_device *at24_nvmem; - -static void at24_setup(struct nvmem_device *mem_acc, void *context) -{ - char buf[16]; - - at24_nvmem = mem_acc; - - /* Read MAC addresses */ - if (nvmem_device_read(at24_nvmem, 0x100, 6, buf) == 6) - memcpy(&laguna_net_data.hwaddr[0], buf, ETH_ALEN); - if (nvmem_device_read(at24_nvmem, 0x106, 6, buf) == 6) - memcpy(&laguna_net_data.hwaddr[1], buf, ETH_ALEN); - if (nvmem_device_read(at24_nvmem, 0x10C, 6, buf) == 6) - memcpy(&laguna_net_data.hwaddr[2], buf, ETH_ALEN); - if (nvmem_device_read(at24_nvmem, 0x112, 6, buf) == 6) - memcpy(&laguna_net_data.hwaddr[3], buf, ETH_ALEN); - - /* Read out Model Information */ - if (nvmem_device_read(at24_nvmem, 0x130, 16, buf) == 16) - memcpy(&laguna_info.model, buf, 16); - if (nvmem_device_read(at24_nvmem, 0x140, 1, buf) == 1) - memcpy(&laguna_info.nor_flash_size, buf, 1); - if (nvmem_device_read(at24_nvmem, 0x141, 1, buf) == 1) - memcpy(&laguna_info.spi_flash_size, buf, 1); - if (nvmem_device_read(at24_nvmem, 0x142, 4, buf) == 4) - memcpy(&laguna_info.config_bitmap, buf, 4); - if (nvmem_device_read(at24_nvmem, 0x146, 4, buf) == 4) - memcpy(&laguna_info.config2_bitmap, buf, 4); -}; - -static struct at24_platform_data laguna_eeprom_info = { - .byte_len = 1024, - .page_size = 16, - .flags = AT24_FLAG_READONLY, - .setup = at24_setup, -}; - -static struct pca953x_platform_data laguna_pca_data = { - .gpio_base = 100, - .irq_base = -1, -}; - -static struct pca953x_platform_data laguna_pca2_data = { - .gpio_base = 116, - .irq_base = -1, -}; - -static struct i2c_board_info __initdata laguna_i2c_devices[] = { - { - I2C_BOARD_INFO("pca9555", 0x23), - .platform_data = &laguna_pca_data, - },{ - I2C_BOARD_INFO("pca9555", 0x27), - .platform_data = &laguna_pca2_data, - },{ - I2C_BOARD_INFO("gsp", 0x29), - },{ - I2C_BOARD_INFO ("24c08",0x50), - .platform_data = &laguna_eeprom_info, - },{ - I2C_BOARD_INFO("ds1672", 0x68), - }, -}; - -/* - * Watchdog - */ - -static struct resource laguna_watchdog_resources[] = { - [0] = { - .start = CNS3XXX_TC11MP_TWD_BASE + 0x100, // CPU0 watchdog - .end = CNS3XXX_TC11MP_TWD_BASE + SZ_4K - 1, - .flags = IORESOURCE_MEM, - }, -}; - -static struct platform_device laguna_watchdog = { - .name = "mpcore_wdt", - .id = PLATFORM_DEVID_NONE, - .num_resources = ARRAY_SIZE(laguna_watchdog_resources), - .resource = laguna_watchdog_resources, -}; - -/* - * GPS PPS - */ -static struct pps_gpio_platform_data laguna_pps_data = { - .gpio_pin = 0, - .gpio_label = "GPS_PPS", - .assert_falling_edge = 0, - .capture_clear = 0, -}; - -static struct platform_device laguna_pps_device = { - .name = "pps-gpio", - .id = PLATFORM_DEVID_NONE, - .dev.platform_data = &laguna_pps_data, -}; - -/* - * GPIO - */ - -static struct gpio laguna_gpio_gw2391[] = { - { 0, GPIOF_IN , "*GPS_PPS" }, - { 1, GPIOF_IN , "*GSC_IRQ#" }, - { 2, GPIOF_IN , "*USB_FAULT#" }, - { 5, GPIOF_OUT_INIT_LOW , "*USB0_PCI_SEL" }, - { 6, GPIOF_OUT_INIT_HIGH, "*USB_VBUS_EN" }, - { 7, GPIOF_OUT_INIT_LOW , "*USB1_PCI_SEL" }, - { 8, GPIOF_OUT_INIT_HIGH, "*PERST#" }, - { 9, GPIOF_OUT_INIT_LOW , "*FP_SER_EN#" }, - { 100, GPIOF_IN , "*USER_PB#" }, - { 103, GPIOF_OUT_INIT_HIGH, "*V5_EN" }, - { 108, GPIOF_IN , "DIO0" }, - { 109, GPIOF_IN , "DIO1" }, - { 110, GPIOF_IN , "DIO2" }, - { 111, GPIOF_IN , "DIO3" }, - { 112, GPIOF_IN , "DIO4" }, -}; - -static struct gpio laguna_gpio_gw2388[] = { - { 0, GPIOF_IN , "*GPS_PPS" }, - { 1, GPIOF_IN , "*GSC_IRQ#" }, - { 3, GPIOF_IN , "*USB_FAULT#" }, - { 6, GPIOF_OUT_INIT_HIGH, "*USB_VBUS_EN" }, - { 7, GPIOF_OUT_INIT_LOW , "*GSM_SEL0" }, - { 8, GPIOF_OUT_INIT_LOW , "*GSM_SEL1" }, - { 9, GPIOF_OUT_INIT_LOW , "*FP_SER_EN" }, - { 100, GPIOF_OUT_INIT_HIGH, "*USER_PB#" }, - { 108, GPIOF_IN , "DIO0" }, - { 109, GPIOF_IN , "DIO1" }, - { 110, GPIOF_IN , "DIO2" }, - { 111, GPIOF_IN , "DIO3" }, - { 112, GPIOF_IN , "DIO4" }, -}; - -static struct gpio laguna_gpio_gw2387[] = { - { 0, GPIOF_IN , "*GPS_PPS" }, - { 1, GPIOF_IN , "*GSC_IRQ#" }, - { 2, GPIOF_IN , "*USB_FAULT#" }, - { 5, GPIOF_OUT_INIT_LOW , "*USB_PCI_SEL" }, - { 6, GPIOF_OUT_INIT_HIGH, "*USB_VBUS_EN" }, - { 7, GPIOF_OUT_INIT_LOW , "*GSM_SEL0" }, - { 8, GPIOF_OUT_INIT_LOW , "*GSM_SEL1" }, - { 9, GPIOF_OUT_INIT_LOW , "*FP_SER_EN" }, - { 100, GPIOF_IN , "*USER_PB#" }, - { 103, GPIOF_OUT_INIT_HIGH, "*V5_EN" }, - { 108, GPIOF_IN , "DIO0" }, - { 109, GPIOF_IN , "DIO1" }, - { 110, GPIOF_IN , "DIO2" }, - { 111, GPIOF_IN , "DIO3" }, - { 112, GPIOF_IN , "DIO4" }, - { 113, GPIOF_IN , "DIO5" }, -}; - -static struct gpio laguna_gpio_gw2386[] = { - { 0, GPIOF_IN , "*GPS_PPS" }, - { 2, GPIOF_IN , "*USB_FAULT#" }, - { 6, GPIOF_OUT_INIT_LOW , "*USB_PCI_SEL" }, - { 7, GPIOF_OUT_INIT_LOW , "*GSM_SEL0" }, - { 8, GPIOF_OUT_INIT_LOW , "*GSM_SEL1" }, - { 9, GPIOF_OUT_INIT_LOW , "*FP_SER_EN" }, - { 108, GPIOF_IN , "DIO0" }, - { 109, GPIOF_IN , "DIO1" }, - { 110, GPIOF_IN , "DIO2" }, - { 111, GPIOF_IN , "DIO3" }, - { 112, GPIOF_IN , "DIO4" }, - { 113, GPIOF_IN , "DIO5" }, -}; - -static struct gpio laguna_gpio_gw2385[] = { - { 0, GPIOF_IN , "*GSC_IRQ#" }, - { 1, GPIOF_OUT_INIT_HIGH, "*USB_HST_VBUS_EN" }, - { 2, GPIOF_IN , "*USB_HST_FAULT#" }, - { 5, GPIOF_IN , "*USB_OTG_FAULT#" }, - { 6, GPIOF_OUT_INIT_LOW , "*USB_HST_PCI_SEL" }, - { 7, GPIOF_OUT_INIT_LOW , "*GSM_SEL0" }, - { 8, GPIOF_OUT_INIT_LOW , "*GSM_SEL1" }, - { 9, GPIOF_OUT_INIT_LOW , "*SER_EN" }, - { 10, GPIOF_IN, "*USER_PB#" }, - { 11, GPIOF_OUT_INIT_HIGH, "*PERST#" }, - { 100, GPIOF_IN , "*USER_PB#" }, - { 103, GPIOF_OUT_INIT_HIGH, "V5_EN" }, -}; - -static struct gpio laguna_gpio_gw2384[] = { - { 0, GPIOF_IN , "*GSC_IRQ#" }, - { 1, GPIOF_OUT_INIT_HIGH, "*USB_HST_VBUS_EN" }, - { 2, GPIOF_IN , "*USB_HST_FAULT#" }, - { 5, GPIOF_IN , "*USB_OTG_FAULT#" }, - { 6, GPIOF_OUT_INIT_LOW , "*USB_HST_PCI_SEL" }, - { 7, GPIOF_OUT_INIT_LOW , "*GSM_SEL0" }, - { 8, GPIOF_OUT_INIT_LOW , "*GSM_SEL1" }, - { 9, GPIOF_OUT_INIT_LOW , "*FP_SER_EN" }, - { 12, GPIOF_OUT_INIT_LOW , "J10_DIOLED0" }, - { 13, GPIOF_OUT_INIT_HIGH, "*I2CMUX_RST#" }, - { 14, GPIOF_OUT_INIT_LOW , "J10_DIOLED1" }, - { 15, GPIOF_OUT_INIT_LOW , "J10_DIOLED2" }, - { 100, GPIOF_IN , "*USER_PB#" }, - { 103, GPIOF_OUT_INIT_HIGH, "V5_EN" }, - { 108, GPIOF_IN , "J9_DIOGSC0" }, -}; - -static struct gpio laguna_gpio_gw2383[] = { - { 0, GPIOF_IN , "*GPS_PPS" }, - { 1, GPIOF_IN , "*GSC_IRQ#" }, - { 2, GPIOF_OUT_INIT_HIGH, "*PCIE_RST#" }, - { 3, GPIOF_IN , "GPIO0" }, - { 8, GPIOF_IN , "GPIO1" }, - { 100, GPIOF_IN , "DIO0" }, - { 101, GPIOF_IN , "DIO1" }, - { 108, GPIOF_IN , "*USER_PB#" }, -}; - -static struct gpio laguna_gpio_gw2382[] = { - { 0, GPIOF_IN , "*GPS_PPS" }, - { 1, GPIOF_IN , "*GSC_IRQ#" }, - { 2, GPIOF_OUT_INIT_HIGH, "*PCIE_RST#" }, - { 3, GPIOF_IN , "GPIO0" }, - { 4, GPIOF_IN , "GPIO1" }, - { 9, GPIOF_OUT_INIT_HIGH, "*USB_VBUS_EN" }, - { 10, GPIOF_OUT_INIT_HIGH, "*USB_PCI_SEL#" }, - { 100, GPIOF_IN , "DIO0" }, - { 101, GPIOF_IN , "DIO1" }, - { 108, GPIOF_IN , "*USER_PB#" }, -}; - -static struct gpio laguna_gpio_gw2380[] = { - { 0, GPIOF_IN , "*GPS_PPS" }, - { 1, GPIOF_IN , "*GSC_IRQ#" }, - { 3, GPIOF_IN , "GPIO0" }, - { 8, GPIOF_IN , "GPIO1" }, - { 100, GPIOF_IN , "DIO0" }, - { 101, GPIOF_IN , "DIO1" }, - { 102, GPIOF_IN , "DIO2" }, - { 103, GPIOF_IN , "DIO3" }, - { 108, GPIOF_IN , "*USER_PB#" }, -}; - -/* - * Initialization - */ -static void __init laguna_init(void) -{ - struct clk *clk; - u32 __iomem *reg; - - clk = clk_register_fixed_rate(NULL, "cpu", NULL, - CLK_IGNORE_UNUSED, - cns3xxx_cpu_clock() * (1000000 / 8)); - clk_register_clkdev(clk, "cpu", NULL); - - platform_device_register(&laguna_watchdog); - - platform_device_register(&laguna_i2c_controller); - - /* Set I2C 0-3 drive strength to 21 mA */ - reg = MISC_IO_PAD_DRIVE_STRENGTH_CTRL_B; - *reg |= 0x300; - - /* Enable SCL/SDA for I2C */ - reg = MISC_GPIOB_PIN_ENABLE_REG; - *reg |= BIT(12) | BIT(13); - - /* Enable MMC/SD pins */ - *reg |= BIT(7) | BIT(8) | BIT(9) | BIT(10) | BIT(11); - - cns3xxx_pwr_clk_en(1 << PM_CLK_GATE_REG_OFFSET_SPI_PCM_I2C); - cns3xxx_pwr_power_up(1 << PM_CLK_GATE_REG_OFFSET_SPI_PCM_I2C); - cns3xxx_pwr_soft_rst(1 << PM_CLK_GATE_REG_OFFSET_SPI_PCM_I2C); - - cns3xxx_pwr_clk_en(CNS3XXX_PWR_CLK_EN(SPI_PCM_I2C)); - cns3xxx_pwr_soft_rst(CNS3XXX_PWR_SOFTWARE_RST(SPI_PCM_I2C)); - - i2c_register_board_info(0, ARRAY_AND_SIZE(laguna_i2c_devices)); - - pm_power_off = cns3xxx_power_off; -} - -static struct map_desc laguna_io_desc[] __initdata = { - { - .virtual = CNS3XXX_UART0_BASE_VIRT, - .pfn = __phys_to_pfn(CNS3XXX_UART0_BASE), - .length = SZ_4K, - .type = MT_DEVICE, - }, -}; - -static void __init laguna_map_io(void) -{ - cns3xxx_map_io(); - iotable_init(ARRAY_AND_SIZE(laguna_io_desc)); - laguna_early_serial_setup(); -} - -static int laguna_register_gpio(struct gpio *array, size_t num) -{ - int i, err, ret; - - ret = 0; - for (i = 0; i < num; i++, array++) { - const char *label = array->label; - if (label[0] == '*') - label++; - err = gpio_request_one(array->gpio, array->flags, label); - if (err) - ret = err; - else { - err = gpio_export(array->gpio, array->label[0] != '*'); - } - } - return ret; -} - -/* allow disabling of external isolated PCIe IRQs */ -static int cns3xxx_pciextirq = 1; -static int __init cns3xxx_pciextirq_disable(char *s) -{ - cns3xxx_pciextirq = 0; - return 1; -} -__setup("noextirq", cns3xxx_pciextirq_disable); - -static int __init laguna_pcie_init_irq(void) -{ - u32 __iomem *mem = (void __iomem *)(CNS3XXX_GPIOB_BASE_VIRT + 0x0004); - u32 reg = (__raw_readl(mem) >> 26) & 0xf; - int irqs[] = { - IRQ_CNS3XXX_EXTERNAL_PIN0, - IRQ_CNS3XXX_EXTERNAL_PIN1, - IRQ_CNS3XXX_EXTERNAL_PIN2, - 154, - }; - - if (!machine_is_gw2388()) - return 0; - - /* Verify GPIOB[26:29] == 0001b indicating support for ext irqs */ - if (cns3xxx_pciextirq && reg != 1) - cns3xxx_pciextirq = 0; - - if (cns3xxx_pciextirq) { - printk("laguna: using isolated PCI interrupts:" - " irq%d/irq%d/irq%d/irq%d\n", - irqs[0], irqs[1], irqs[2], irqs[3]); - cns3xxx_pcie_set_irqs(0, irqs); - } else { - printk("laguna: using shared PCI interrupts: irq%d\n", - IRQ_CNS3XXX_PCIE0_DEVICE); - } - - return 0; -} -subsys_initcall(laguna_pcie_init_irq); - -static int __init laguna_model_setup(void) -{ - u32 __iomem *mem; - u32 reg; - - if (!machine_is_gw2388()) - return 0; - - printk("Running on Gateworks Laguna %s\n", laguna_info.model); - cns3xxx_gpio_init( 0, 32, CNS3XXX_GPIOA_BASE_VIRT, IRQ_CNS3XXX_GPIOA, - NR_IRQS_CNS3XXX); - - /* - * If pcie external interrupts are supported and desired - * configure IRQ types and configure pin function. - * Note that cns3xxx_pciextirq is enabled by default, but can be - * unset via the 'noextirq' kernel param or by laguna_pcie_init() if - * the baseboard model does not support this hardware feature. - */ - if (cns3xxx_pciextirq) { - mem = (void __iomem *)(CNS3XXX_MISC_BASE_VIRT + 0x0018); - reg = __raw_readl(mem); - /* GPIO26 is gpio, EXT_INT[0:2] not gpio func */ - reg &= ~0x3c000000; - reg |= 0x38000000; - __raw_writel(reg, mem); - - cns3xxx_gpio_init(32, 32, CNS3XXX_GPIOB_BASE_VIRT, - IRQ_CNS3XXX_GPIOB, NR_IRQS_CNS3XXX + 32); - - irq_set_irq_type(154, IRQ_TYPE_LEVEL_LOW); - irq_set_irq_type(93, IRQ_TYPE_LEVEL_HIGH); - irq_set_irq_type(94, IRQ_TYPE_LEVEL_HIGH); - irq_set_irq_type(95, IRQ_TYPE_LEVEL_HIGH); - } else { - cns3xxx_gpio_init(32, 32, CNS3XXX_GPIOB_BASE_VIRT, - IRQ_CNS3XXX_GPIOB, NR_IRQS_CNS3XXX + 32); - } - - if (strncmp(laguna_info.model, "GW", 2) == 0) { - if (laguna_info.config_bitmap & ETH0_LOAD) - laguna_net_data.ports |= BIT(0); - if (laguna_info.config_bitmap & ETH1_LOAD) - laguna_net_data.ports |= BIT(1); - if (laguna_info.config_bitmap & ETH2_LOAD) - laguna_net_data.ports |= BIT(2); - if (laguna_net_data.ports) - platform_device_register(&laguna_net_device); - - if ((laguna_info.config_bitmap & SATA0_LOAD) || - (laguna_info.config_bitmap & SATA1_LOAD)) - cns3xxx_ahci_init(); - - if (laguna_info.config_bitmap & (USB0_LOAD)) { - cns3xxx_pwr_power_up(1 << PM_PLL_HM_PD_CTRL_REG_OFFSET_PLL_USB); - - /* DRVVBUS pins share with GPIOA */ - mem = (void __iomem *)(CNS3XXX_MISC_BASE_VIRT + 0x0014); - reg = __raw_readl(mem); - reg |= 0x8; - __raw_writel(reg, mem); - - /* Enable OTG */ - mem = (void __iomem *)(CNS3XXX_MISC_BASE_VIRT + 0x0808); - reg = __raw_readl(mem); - reg &= ~(1 << 10); - __raw_writel(reg, mem); - - platform_device_register(&cns3xxx_usb_otg_device); - } - - if (laguna_info.config_bitmap & (USB1_LOAD)) { - platform_device_register(&cns3xxx_usb_ehci_device); - platform_device_register(&cns3xxx_usb_ohci_device); - } - - if (laguna_info.config_bitmap & (SD_LOAD)) - cns3xxx_sdhci_init(); - - if (laguna_info.config_bitmap & (UART0_LOAD)) - laguna_uart.num_resources = 1; - if (laguna_info.config_bitmap & (UART1_LOAD)) - laguna_uart.num_resources = 2; - if (laguna_info.config_bitmap & (UART2_LOAD)) - laguna_uart.num_resources = 3; - platform_device_register(&laguna_uart); - - if (laguna_info.config2_bitmap & (NOR_FLASH_LOAD)) { - laguna_nor_partitions[2].size = - (SZ_4M << laguna_info.nor_flash_size) - - laguna_nor_partitions[2].offset; - laguna_nor_res.end = CNS3XXX_FLASH_BASE + - laguna_nor_partitions[2].offset + - laguna_nor_partitions[2].size - 1; - platform_device_register(&laguna_nor_pdev); - } - - if (laguna_info.config2_bitmap & (SPI_FLASH_LOAD)) { - laguna_spi_partitions[2].size = - (SZ_2M << laguna_info.spi_flash_size) - - laguna_spi_partitions[2].offset; - spi_register_board_info(ARRAY_AND_SIZE(laguna_spi_devices)); - } - - if ((laguna_info.config_bitmap & SPI0_LOAD) || - (laguna_info.config_bitmap & SPI1_LOAD)) - platform_device_register(&laguna_spi_controller); - - if (laguna_info.config2_bitmap & GPS_LOAD) - platform_device_register(&laguna_pps_device); - - /* - * Do any model specific setup not known by the bitmap by matching - * the first 6 characters of the model name - */ - - if ( (strncmp(laguna_info.model, "GW2388", 6) == 0) - || (strncmp(laguna_info.model, "GW2389", 6) == 0) ) - { - // configure GPIO's - laguna_register_gpio(ARRAY_AND_SIZE(laguna_gpio_gw2388)); - // configure LED's - laguna_gpio_leds_data.num_leds = 2; - } else if (strncmp(laguna_info.model, "GW2387", 6) == 0) { - // configure GPIO's - laguna_register_gpio(ARRAY_AND_SIZE(laguna_gpio_gw2387)); - // configure LED's - laguna_gpio_leds_data.num_leds = 2; - } else if (strncmp(laguna_info.model, "GW2386", 6) == 0) { - // configure GPIO's - laguna_register_gpio(ARRAY_AND_SIZE(laguna_gpio_gw2386)); - // configure LED's - laguna_gpio_leds_data.num_leds = 2; - } else if (strncmp(laguna_info.model, "GW2385", 6) == 0) { - // configure GPIO's - laguna_register_gpio(ARRAY_AND_SIZE(laguna_gpio_gw2385)); - // configure LED's - laguna_gpio_leds[0].gpio = 115; - laguna_gpio_leds[1].gpio = 12; - laguna_gpio_leds[1].name = "red"; - laguna_gpio_leds[1].active_low = 0, - laguna_gpio_leds[2].gpio = 14; - laguna_gpio_leds[2].name = "green"; - laguna_gpio_leds[2].active_low = 0, - laguna_gpio_leds[3].gpio = 15; - laguna_gpio_leds[3].name = "blue"; - laguna_gpio_leds[3].active_low = 0, - laguna_gpio_leds_data.num_leds = 4; - } else if ( (strncmp(laguna_info.model, "GW2384", 6) == 0) - || (strncmp(laguna_info.model, "GW2394", 6) == 0) ) - { - // configure GPIO's - laguna_register_gpio(ARRAY_AND_SIZE(laguna_gpio_gw2384)); - // configure LED's - laguna_gpio_leds_data.num_leds = 1; - } else if (strncmp(laguna_info.model, "GW2383", 6) == 0) { - // configure GPIO's - laguna_register_gpio(ARRAY_AND_SIZE(laguna_gpio_gw2383)); - // configure LED's - laguna_gpio_leds[0].gpio = 107; - laguna_gpio_leds_data.num_leds = 1; - } else if (strncmp(laguna_info.model, "GW2382", 6) == 0) { - // configure GPIO's - laguna_register_gpio(ARRAY_AND_SIZE(laguna_gpio_gw2382)); - // configure LED's - laguna_gpio_leds[0].gpio = 107; - laguna_gpio_leds_data.num_leds = 1; - } else if (strncmp(laguna_info.model, "GW2380", 6) == 0) { - // configure GPIO's - laguna_register_gpio(ARRAY_AND_SIZE(laguna_gpio_gw2380)); - // configure LED's - laguna_gpio_leds[0].gpio = 107; - laguna_gpio_leds[1].gpio = 106; - laguna_gpio_leds_data.num_leds = 2; - } else if ( (strncmp(laguna_info.model, "GW2391", 6) == 0) - || (strncmp(laguna_info.model, "GW2393", 6) == 0) ) - { - // configure GPIO's - laguna_register_gpio(ARRAY_AND_SIZE(laguna_gpio_gw2391)); - // configure LED's - laguna_gpio_leds_data.num_leds = 2; - } - platform_device_register(&laguna_gpio_leds_device); - } else { - // Do some defaults here, not sure what yet - } - return 0; -} -late_initcall(laguna_model_setup); - -MACHINE_START(GW2388, "Gateworks Corporation Laguna Platform") - .smp = smp_ops(cns3xxx_smp_ops), - .atag_offset = 0x100, - .map_io = laguna_map_io, - .init_irq = cns3xxx_init_irq, - .init_time = cns3xxx_timer_init, - .init_machine = laguna_init, - .init_late = cns3xxx_pcie_init_late, - .restart = cns3xxx_restart, -MACHINE_END diff --git a/target/linux/cns3xxx/files/arch/arm/mach-cns3xxx/platsmp.c b/target/linux/cns3xxx/files/arch/arm/mach-cns3xxx/platsmp.c deleted file mode 100644 index cb81d1e752..0000000000 --- a/target/linux/cns3xxx/files/arch/arm/mach-cns3xxx/platsmp.c +++ /dev/null @@ -1,327 +0,0 @@ -/* - * linux/arch/arm/mach-cns3xxx/platsmp.c - * - * Copyright (C) 2002 ARM Ltd. - * Copyright 2012 Gateworks Corporation - * Chris Lang - * Tim Harvey - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include "cns3xxx.h" - -static struct fiq_handler fh = { - .name = "cns3xxx-fiq" -}; - -struct fiq_req { - union { - struct { - const void *addr; - size_t size; - } map; - struct { - const void *addr; - size_t size; - } unmap; - struct { - const void *start; - const void *end; - } flush; - }; - volatile uint flags; - void __iomem *reg; -} ____cacheline_aligned; - -extern unsigned int fiq_number[2]; - -DEFINE_PER_CPU(struct fiq_req, fiq_data); - -#define FIQ_ENABLED 0x80000000 -#define FIQ_GENERATE 0x00010000 -#define CNS3XXX_MAP_AREA 0x01000000 -#define CNS3XXX_UNMAP_AREA 0x02000000 -#define CNS3XXX_FLUSH_RANGE 0x03000000 - -extern void cns3xxx_secondary_startup(void); -extern unsigned char cns3xxx_fiq_start, cns3xxx_fiq_end; - -#define SCU_CPU_STATUS 0x08 -static void __iomem *scu_base; - -static inline void cns3xxx_set_fiq_regs(unsigned int cpu) -{ - struct pt_regs FIQ_regs; - struct fiq_req *fiq_req = &per_cpu(fiq_data, !cpu); - - FIQ_regs.ARM_r8 = 0; - FIQ_regs.ARM_ip = (unsigned int)fiq_req; - FIQ_regs.ARM_sp = (int) MISC_FIQ_CPU(!cpu); - fiq_req->reg = MISC_FIQ_CPU(!cpu); - - set_fiq_regs(&FIQ_regs); -} - -static void __init cns3xxx_init_fiq(void) -{ - void *fiqhandler_start; - unsigned int fiqhandler_length; - int ret; - - fiqhandler_start = &cns3xxx_fiq_start; - fiqhandler_length = &cns3xxx_fiq_end - &cns3xxx_fiq_start; - - ret = claim_fiq(&fh); - if (ret) - return; - - set_fiq_handler(fiqhandler_start, fiqhandler_length); -} - - -/* - * Write pen_release in a way that is guaranteed to be visible to all - * observers, irrespective of whether they're taking part in coherency - * or not. This is necessary for the hotplug code to work reliably. - */ -static void write_pen_release(int val) -{ - pen_release = val; - smp_wmb(); - __cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release)); - outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1)); -} - -static DEFINE_SPINLOCK(boot_lock); - -static void cns3xxx_secondary_init(unsigned int cpu) -{ - /* - * Setup Secondary Core FIQ regs - */ - cns3xxx_set_fiq_regs(1); - - /* - * let the primary processor know we're out of the - * pen, then head off into the C entry point - */ - write_pen_release(-1); - - /* - * Synchronise with the boot thread. - */ - spin_lock(&boot_lock); - spin_unlock(&boot_lock); -} - -static int cns3xxx_boot_secondary(unsigned int cpu, struct task_struct *idle) -{ - unsigned long timeout; - - /* - * Set synchronisation state between this boot processor - * and the secondary one - */ - spin_lock(&boot_lock); - - /* - * The secondary processor is waiting to be released from - * the holding pen - release it, then wait for it to flag - * that it has been released by resetting pen_release. - * - * Note that "pen_release" is the hardware CPU ID, whereas - * "cpu" is Linux's internal ID. - */ - write_pen_release(cpu); - - /* - * Send the secondary CPU a soft interrupt, thereby causing - * the boot monitor to read the system wide flags register, - * and branch to the address found there. - */ - arch_send_wakeup_ipi_mask(cpumask_of(cpu));; - - timeout = jiffies + (1 * HZ); - while (time_before(jiffies, timeout)) { - smp_rmb(); - if (pen_release == -1) - break; - - udelay(10); - } - - /* - * now the secondary core is starting up let it run its - * calibrations, then wait for it to finish - */ - spin_unlock(&boot_lock); - - return pen_release != -1 ? -ENOSYS : 0; -} - -/* - * Initialise the CPU possible map early - this describes the CPUs - * which may be present or become present in the system. - */ -static void __init cns3xxx_smp_init_cpus(void) -{ - unsigned int i, ncores; - unsigned int status; - - scu_base = (void __iomem *) CNS3XXX_TC11MP_SCU_BASE_VIRT; - - /* for CNS3xxx SCU_CPU_STATUS must be examined instead of SCU_CONFIGURATION - * used in scu_get_core_count - */ - status = __raw_readl(scu_base + SCU_CPU_STATUS); - for (i = 0; i < NR_CPUS+1; i++) { - if (((status >> (i*2)) & 0x3) == 0) - set_cpu_possible(i, true); - else - break; - } - ncores = i; -} - -static void __init cns3xxx_smp_prepare_cpus(unsigned int max_cpus) -{ - /* - * enable SCU - */ - scu_enable(scu_base); - - /* - * Write the address of secondary startup into the - * system-wide flags register. The boot monitor waits - * until it receives a soft interrupt, and then the - * secondary CPU branches to this address. - */ - __raw_writel(virt_to_phys(cns3xxx_secondary_startup), - (void __iomem *)(CNS3XXX_MISC_BASE_VIRT + 0x0600)); - - /* - * Setup FIQ's for main cpu - */ - cns3xxx_init_fiq(); - cns3xxx_set_fiq_regs(0); -} - -extern void v6_dma_map_area(const void *, size_t, int); -extern void v6_dma_unmap_area(const void *, size_t, int); -extern void v6_dma_flush_range(const void *, const void *); -extern void v6_flush_kern_dcache_area(void *, size_t); - -void fiq_dma_map_area(const void *addr, size_t size, int dir) -{ - unsigned long flags; - struct fiq_req *req; - - raw_local_irq_save(flags); - /* currently, not possible to take cpu0 down, so only check cpu1 */ - if (!cpu_online(1)) { - raw_local_irq_restore(flags); - v6_dma_map_area(addr, size, dir); - return; - } - - req = this_cpu_ptr(&fiq_data); - req->map.addr = addr; - req->map.size = size; - req->flags = dir | CNS3XXX_MAP_AREA; - smp_mb(); - - writel_relaxed(FIQ_GENERATE, req->reg); - - v6_dma_map_area(addr, size, dir); - while (req->flags) - barrier(); - - raw_local_irq_restore(flags); -} - -void fiq_dma_unmap_area(const void *addr, size_t size, int dir) -{ - unsigned long flags; - struct fiq_req *req; - - raw_local_irq_save(flags); - /* currently, not possible to take cpu0 down, so only check cpu1 */ - if (!cpu_online(1)) { - raw_local_irq_restore(flags); - v6_dma_unmap_area(addr, size, dir); - return; - } - - req = this_cpu_ptr(&fiq_data); - req->unmap.addr = addr; - req->unmap.size = size; - req->flags = dir | CNS3XXX_UNMAP_AREA; - smp_mb(); - - writel_relaxed(FIQ_GENERATE, req->reg); - - v6_dma_unmap_area(addr, size, dir); - while (req->flags) - barrier(); - - raw_local_irq_restore(flags); -} - -void fiq_dma_flush_range(const void *start, const void *end) -{ - unsigned long flags; - struct fiq_req *req; - - raw_local_irq_save(flags); - /* currently, not possible to take cpu0 down, so only check cpu1 */ - if (!cpu_online(1)) { - raw_local_irq_restore(flags); - v6_dma_flush_range(start, end); - return; - } - - req = this_cpu_ptr(&fiq_data); - - req->flush.start = start; - req->flush.end = end; - req->flags = CNS3XXX_FLUSH_RANGE; - smp_mb(); - - writel_relaxed(FIQ_GENERATE, req->reg); - - v6_dma_flush_range(start, end); - - while (req->flags) - barrier(); - - raw_local_irq_restore(flags); -} - -void fiq_flush_kern_dcache_area(void *addr, size_t size) -{ - fiq_dma_flush_range(addr, addr + size); -} - -struct smp_operations cns3xxx_smp_ops __initdata = { - .smp_init_cpus = cns3xxx_smp_init_cpus, - .smp_prepare_cpus = cns3xxx_smp_prepare_cpus, - .smp_secondary_init = cns3xxx_secondary_init, - .smp_boot_secondary = cns3xxx_boot_secondary, -}; diff --git a/target/linux/cns3xxx/files/drivers/i2c/busses/i2c-cns3xxx.c b/target/linux/cns3xxx/files/drivers/i2c/busses/i2c-cns3xxx.c deleted file mode 100644 index 7acff37d26..0000000000 --- a/target/linux/cns3xxx/files/drivers/i2c/busses/i2c-cns3xxx.c +++ /dev/null @@ -1,374 +0,0 @@ -/* - * Cavium CNS3xxx I2C Host Controller - * - * Copyright 2010 Cavium Network - * Copyright 2012 Gateworks Corporation - * Chris Lang - * Tim Harvey - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, Version 2, as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * We need the memory map - */ - -#define I2C_MEM_MAP_ADDR(x) (i2c->base + x) -#define I2C_MEM_MAP_VALUE(x) (*((unsigned int volatile*)I2C_MEM_MAP_ADDR(x))) - -#define I2C_CONTROLLER_REG I2C_MEM_MAP_VALUE(0x00) -#define I2C_TIME_OUT_REG I2C_MEM_MAP_VALUE(0x04) -#define I2C_SLAVE_ADDRESS_REG I2C_MEM_MAP_VALUE(0x08) -#define I2C_WRITE_DATA_REG I2C_MEM_MAP_VALUE(0x0C) -#define I2C_READ_DATA_REG I2C_MEM_MAP_VALUE(0x10) -#define I2C_INTERRUPT_STATUS_REG I2C_MEM_MAP_VALUE(0x14) -#define I2C_INTERRUPT_ENABLE_REG I2C_MEM_MAP_VALUE(0x18) -#define I2C_TWI_OUT_DLY_REG I2C_MEM_MAP_VALUE(0x1C) - -#define I2C_BUS_ERROR_FLAG (0x1) -#define I2C_ACTION_DONE_FLAG (0x2) - -#define CNS3xxx_I2C_ENABLE() (I2C_CONTROLLER_REG) |= ((unsigned int)0x1 << 31) -#define CNS3xxx_I2C_DISABLE() (I2C_CONTROLLER_REG) &= ~((unsigned int)0x1 << 31) -#define CNS3xxx_I2C_ENABLE_INTR() (I2C_INTERRUPT_ENABLE_REG) |= 0x03 -#define CNS3xxx_I2C_DISABLE_INTR() (I2C_INTERRUPT_ENABLE_REG) &= 0xfc - -#define TWI_TIMEOUT (10*HZ) -#define I2C_100KHZ 100000 -#define I2C_200KHZ 200000 -#define I2C_300KHZ 300000 -#define I2C_400KHZ 400000 - -#define CNS3xxx_I2C_CLK I2C_100KHZ - -#define STATE_DONE 1 -#define STATE_ERROR 2 - -struct cns3xxx_i2c { - struct device *dev; - void __iomem *base; /* virtual */ - wait_queue_head_t wait; - struct i2c_adapter adap; - struct i2c_msg *msg; - u8 state; /* see STATE_ */ - u8 error; /* see TWI_STATUS register */ - int rd_wr_len; - u8 *buf; -}; - -static u32 cns3xxx_i2c_func(struct i2c_adapter *adap) -{ - return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; -} - -static int -cns3xxx_i2c_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg) -{ - struct cns3xxx_i2c *i2c = i2c_get_adapdata(adap); - int i, j; - u8 buf[1] = { 0 }; - - if (msg->len == 0) { - /* - * We are probably doing a probe for a device here, - * so set the length to one, and data to 0 - */ - msg->len = 1; - i2c->buf = buf; - } else { - i2c->buf = msg->buf; - } - - if (msg->flags & I2C_M_TEN) { - printk - ("%s:%d: Presently the driver does not handle extended addressing\n", - __FUNCTION__, __LINE__); - return -EINVAL; - } - i2c->msg = msg; - - for (i = 0; i < msg->len; i++) { - if (msg->len - i >= 4) - i2c->rd_wr_len = 3; - else - i2c->rd_wr_len = msg->len - i - 1; - - // Set Data Width and TWI_EN - I2C_CONTROLLER_REG = 0x80000000 | (i2c->rd_wr_len << 2) | (i2c->rd_wr_len); - - // Clear Write Reg - I2C_WRITE_DATA_REG = 0; - - // Set the slave address - I2C_SLAVE_ADDRESS_REG = (msg->addr << 1); - - // Are we Writing - if (!(msg->flags & I2C_M_RD)) { - I2C_CONTROLLER_REG |= (1 << 4); - if (i != 0) { - /* - * We need to set the address in the first byte. - * The base address is going to be in buf[0] and then - * it needs to be incremented by i - 1. - */ - i2c->buf--; - *i2c->buf = buf[0] + i - 1; - - if (i2c->rd_wr_len < 3) { - i += i2c->rd_wr_len; - i2c->rd_wr_len++; - I2C_CONTROLLER_REG = 0x80000000 | (1 << 4) | (i2c->rd_wr_len << 2) | (i2c->rd_wr_len); - } else { - i += i2c->rd_wr_len - 1; - } - } else { - i += i2c->rd_wr_len; - buf[0] = *i2c->buf; - } - for (j = 0; j <= i2c->rd_wr_len; j++) { - I2C_WRITE_DATA_REG |= ((*i2c->buf++) << (8 * j)); - } - } else { - i += i2c->rd_wr_len; - } - - // Start the Transfer - i2c->state = 0; // Clear out the State - i2c->error = 0; - I2C_CONTROLLER_REG |= (1 << 6); - - if (wait_event_timeout(i2c->wait, (i2c->state == STATE_ERROR) || - (i2c->state == STATE_DONE), TWI_TIMEOUT)) { - if (i2c->state == STATE_ERROR) { - dev_dbg(i2c->dev, "controller error: 0x%2x", i2c->error); - return -EAGAIN; // try again - } - } else { - dev_err(i2c->dev, "controller timed out " - "waiting for start condition to finish\n"); - return -ETIMEDOUT; - } - } - return 0; -} - -static int -cns3xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) -{ - int i; - int ret; - for (i = 0; i < num; i++) - { - ret = cns3xxx_i2c_xfer_msg(adap, &msgs[i]); - if (ret < 0) { - return ret; - } - } - return num; -} - - -static struct i2c_algorithm cns3xxx_i2c_algo = { - .master_xfer = cns3xxx_i2c_xfer, - .functionality = cns3xxx_i2c_func, -}; - -static struct i2c_adapter cns3xxx_i2c_adapter = { - .owner = THIS_MODULE, - .algo = &cns3xxx_i2c_algo, - .algo_data = NULL, - .nr = 0, - .name = "CNS3xxx I2C 0", - .retries = 5, -}; - -static void cns3xxx_i2c_adapter_init(struct cns3xxx_i2c *i2c) -{ - struct clk *clk; - - clk = devm_clk_get(i2c->dev, "cpu"); - if (WARN_ON(!clk)) - return; - - /* Disable the I2C */ - I2C_CONTROLLER_REG = 0; /* Disabled the I2C */ - - /* Check the Reg Dump when testing */ - I2C_TIME_OUT_REG = - (((((clk_get_rate(clk) / (2 * CNS3xxx_I2C_CLK)) - - 1) & 0x3FF) << 8) | (1 << 7) | 0x7F); - I2C_TWI_OUT_DLY_REG |= 0x3; - - /* Enable The Interrupt */ - CNS3xxx_I2C_ENABLE_INTR(); - - /* Clear Interrupt Status (0x2 | 0x1) */ - I2C_INTERRUPT_STATUS_REG |= (I2C_ACTION_DONE_FLAG | I2C_BUS_ERROR_FLAG); - - /* Enable the I2C Controller */ - CNS3xxx_I2C_ENABLE(); -} - -static irqreturn_t cns3xxx_i2c_isr(int irq, void *dev_id) -{ - struct cns3xxx_i2c *i2c = dev_id; - int i; - uint32_t stat = I2C_INTERRUPT_STATUS_REG; - - /* Clear Interrupt */ - I2C_INTERRUPT_STATUS_REG |= 0x1; - - if (stat & I2C_BUS_ERROR_FLAG) { - i2c->state = STATE_ERROR; - i2c->error = (I2C_INTERRUPT_STATUS_REG & 0xff00)>>8; - } else { - if (i2c->msg->flags & I2C_M_RD) { - for (i = 0; i <= i2c->rd_wr_len; i++) - { - *i2c->buf++ = ((I2C_READ_DATA_REG >> (8 * i)) & 0xff); - } - } - i2c->state = STATE_DONE; - } - wake_up(&i2c->wait); - return IRQ_HANDLED; -} - -static int cns3xxx_i2c_probe(struct platform_device *pdev) -{ - struct cns3xxx_i2c *i2c; - struct resource *res, *res2; - int ret; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - printk("%s: IORESOURCE_MEM not defined \n", __FUNCTION__); - return -ENODEV; - } - - res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res2) { - printk("%s: IORESOURCE_IRQ not defined \n", __FUNCTION__); - return -ENODEV; - } - - i2c = kzalloc(sizeof(*i2c), GFP_KERNEL); - if (!i2c) - return -ENOMEM; - - if (!request_mem_region(res->start, res->end - res->start + 1, - pdev->name)) { - dev_err(&pdev->dev, "Memory region busy\n"); - ret = -EBUSY; - goto request_mem_failed; - } - - i2c->dev = &pdev->dev; - i2c->base = ioremap(res->start, res->end - res->start + 1); - if (!i2c->base) { - dev_err(&pdev->dev, "Unable to map registers\n"); - ret = -EIO; - goto map_failed; - } - - cns3xxx_i2c_adapter_init(i2c); - - init_waitqueue_head(&i2c->wait); - ret = request_irq(res2->start, cns3xxx_i2c_isr, 0, pdev->name, i2c); - if (ret) { - dev_err(&pdev->dev, "Cannot claim IRQ\n"); - goto request_irq_failed; - } - - platform_set_drvdata(pdev, i2c); - i2c->adap = cns3xxx_i2c_adapter; - i2c_set_adapdata(&i2c->adap, i2c); - i2c->adap.dev.parent = &pdev->dev; - - /* add i2c adapter to i2c tree */ - ret = i2c_add_numbered_adapter(&i2c->adap); - if (ret) { - dev_err(&pdev->dev, "Failed to add adapter\n"); - goto add_adapter_failed; - } - - return 0; - - add_adapter_failed: - free_irq(res2->start, i2c); - request_irq_failed: - iounmap(i2c->base); - map_failed: - release_mem_region(res->start, res->end - res->start + 1); - request_mem_failed: - kfree(i2c); - - return ret; -} - -static int cns3xxx_i2c_remove(struct platform_device *pdev) -{ - struct cns3xxx_i2c *i2c = platform_get_drvdata(pdev); - struct resource *res; - - /* disable i2c logic */ - CNS3xxx_I2C_DISABLE_INTR(); - CNS3xxx_I2C_DISABLE(); - /* remove adapter & data */ - i2c_del_adapter(&i2c->adap); - platform_set_drvdata(pdev, NULL); - - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (res) - free_irq(res->start, i2c); - - iounmap(i2c->base); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res) - release_mem_region(res->start, res->end - res->start + 1); - - kfree(i2c); - - return 0; -} - -static struct platform_driver cns3xxx_i2c_driver = { - .probe = cns3xxx_i2c_probe, - .remove = cns3xxx_i2c_remove, - .driver = { - .owner = THIS_MODULE, - .name = "cns3xxx-i2c", - }, -}; - -static int __init cns3xxx_i2c_init(void) -{ - return platform_driver_register(&cns3xxx_i2c_driver); -} - -static void __exit cns3xxx_i2c_exit(void) -{ - platform_driver_unregister(&cns3xxx_i2c_driver); -} - -module_init(cns3xxx_i2c_init); -module_exit(cns3xxx_i2c_exit); - -MODULE_AUTHOR("Cavium Networks"); -MODULE_DESCRIPTION("Cavium CNS3XXX I2C Controller"); -MODULE_LICENSE("GPL"); diff --git a/target/linux/cns3xxx/files/drivers/net/ethernet/cavium/Kconfig b/target/linux/cns3xxx/files/drivers/net/ethernet/cavium/Kconfig deleted file mode 100644 index 79eebe2863..0000000000 --- a/target/linux/cns3xxx/files/drivers/net/ethernet/cavium/Kconfig +++ /dev/null @@ -1,24 +0,0 @@ -config NET_VENDOR_CAVIUM - bool "Cavium devices" - default y - depends on ARCH_CNS3XXX - ---help--- - If you have a network (Ethernet) chipset belonging to this class, - say Y. - - Note that the answer to this question does not directly affect - the kernel: saying N will just case the configurator to skip all - the questions regarding AMD chipsets. If you say Y, you will be asked - for your specific chipset/driver in the following questions. - -if NET_VENDOR_CAVIUM - -config CNS3XXX_ETH - tristate "Cavium CNS3xxx Ethernet support" - depends on ARCH_CNS3XXX - select PHYLIB - help - Say Y here if you want to use built-in Ethernet ports - on CNS3XXX processor. - -endif diff --git a/target/linux/cns3xxx/files/drivers/net/ethernet/cavium/Makefile b/target/linux/cns3xxx/files/drivers/net/ethernet/cavium/Makefile deleted file mode 100644 index badd2404a6..0000000000 --- a/target/linux/cns3xxx/files/drivers/net/ethernet/cavium/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -# -# Makefile for the Cavium ethernet device drivers. -# - -obj-$(CONFIG_CNS3XXX_ETH) += cns3xxx_eth.o diff --git a/target/linux/cns3xxx/files/drivers/net/ethernet/cavium/cns3xxx_eth.c b/target/linux/cns3xxx/files/drivers/net/ethernet/cavium/cns3xxx_eth.c deleted file mode 100644 index 4556499429..0000000000 --- a/target/linux/cns3xxx/files/drivers/net/ethernet/cavium/cns3xxx_eth.c +++ /dev/null @@ -1,1325 +0,0 @@ -/* - * Cavium CNS3xxx Gigabit driver for Linux - * - * Copyright 2011 Gateworks Corporation - * Chris Lang - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License - * as published by the Free Software Foundation. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRV_NAME "cns3xxx_eth" - -#define RX_DESCS 256 -#define TX_DESCS 128 -#define TX_DESC_RESERVE 20 - -#define RX_POOL_ALLOC_SIZE (sizeof(struct rx_desc) * RX_DESCS) -#define TX_POOL_ALLOC_SIZE (sizeof(struct tx_desc) * TX_DESCS) -#define REGS_SIZE 336 - -#define RX_BUFFER_ALIGN 64 -#define RX_BUFFER_ALIGN_MASK (~(RX_BUFFER_ALIGN - 1)) - -#define SKB_HEAD_ALIGN (((PAGE_SIZE - NET_SKB_PAD) % RX_BUFFER_ALIGN) + NET_SKB_PAD + NET_IP_ALIGN) -#define RX_SEGMENT_ALLOC_SIZE 2048 -#define RX_SEGMENT_BUFSIZE (SKB_WITH_OVERHEAD(RX_SEGMENT_ALLOC_SIZE)) -#define RX_SEGMENT_MRU (((RX_SEGMENT_BUFSIZE - SKB_HEAD_ALIGN) & RX_BUFFER_ALIGN_MASK) - NET_IP_ALIGN) -#define MAX_MTU 9500 - -#define NAPI_WEIGHT 64 - -/* MDIO Defines */ -#define MDIO_CMD_COMPLETE 0x00008000 -#define MDIO_WRITE_COMMAND 0x00002000 -#define MDIO_READ_COMMAND 0x00004000 -#define MDIO_REG_OFFSET 8 -#define MDIO_VALUE_OFFSET 16 - -/* Descritor Defines */ -#define END_OF_RING 0x40000000 -#define FIRST_SEGMENT 0x20000000 -#define LAST_SEGMENT 0x10000000 -#define FORCE_ROUTE 0x04000000 -#define UDP_CHECKSUM 0x00020000 -#define TCP_CHECKSUM 0x00010000 - -/* Port Config Defines */ -#define PORT_BP_ENABLE 0x00020000 -#define PORT_DISABLE 0x00040000 -#define PORT_LEARN_DIS 0x00080000 -#define PORT_BLOCK_STATE 0x00100000 -#define PORT_BLOCK_MODE 0x00200000 - -#define PROMISC_OFFSET 29 - -/* Global Config Defines */ -#define UNKNOWN_VLAN_TO_CPU 0x02000000 -#define ACCEPT_CRC_PACKET 0x00200000 -#define CRC_STRIPPING 0x00100000 - -/* VLAN Config Defines */ -#define NIC_MODE 0x00008000 -#define VLAN_UNAWARE 0x00000001 - -/* DMA AUTO Poll Defines */ -#define TS_POLL_EN 0x00000020 -#define TS_SUSPEND 0x00000010 -#define FS_POLL_EN 0x00000002 -#define FS_SUSPEND 0x00000001 - -/* DMA Ring Control Defines */ -#define QUEUE_THRESHOLD 0x000000f0 -#define CLR_FS_STATE 0x80000000 - -/* Interrupt Status Defines */ -#define MAC0_STATUS_CHANGE 0x00004000 -#define MAC1_STATUS_CHANGE 0x00008000 -#define MAC2_STATUS_CHANGE 0x00010000 -#define MAC0_RX_ERROR 0x00100000 -#define MAC1_RX_ERROR 0x00200000 -#define MAC2_RX_ERROR 0x00400000 - -struct tx_desc -{ - u32 sdp; /* segment data pointer */ - - union { - struct { - u32 sdl:16; /* segment data length */ - u32 tco:1; - u32 uco:1; - u32 ico:1; - u32 rsv_1:3; /* reserve */ - u32 pri:3; - u32 fp:1; /* force priority */ - u32 fr:1; - u32 interrupt:1; - u32 lsd:1; - u32 fsd:1; - u32 eor:1; - u32 cown:1; - }; - u32 config0; - }; - - union { - struct { - u32 ctv:1; - u32 stv:1; - u32 sid:4; - u32 inss:1; - u32 dels:1; - u32 rsv_2:9; - u32 pmap:5; - u32 mark:3; - u32 ewan:1; - u32 fewan:1; - u32 rsv_3:5; - }; - u32 config1; - }; - - union { - struct { - u32 c_vid:12; - u32 c_cfs:1; - u32 c_pri:3; - u32 s_vid:12; - u32 s_dei:1; - u32 s_pri:3; - }; - u32 config2; - }; - - u8 alignment[16]; /* for 32 byte */ -}; - -struct rx_desc -{ - u32 sdp; /* segment data pointer */ - - union { - struct { - u32 sdl:16; /* segment data length */ - u32 l4f:1; - u32 ipf:1; - u32 prot:4; - u32 hr:6; - u32 lsd:1; - u32 fsd:1; - u32 eor:1; - u32 cown:1; - }; - u32 config0; - }; - - union { - struct { - u32 ctv:1; - u32 stv:1; - u32 unv:1; - u32 iwan:1; - u32 exdv:1; - u32 e_wan:1; - u32 rsv_1:2; - u32 sp:3; - u32 crc_err:1; - u32 un_eth:1; - u32 tc:2; - u32 rsv_2:1; - u32 ip_offset:5; - u32 rsv_3:11; - }; - u32 config1; - }; - - union { - struct { - u32 c_vid:12; - u32 c_cfs:1; - u32 c_pri:3; - u32 s_vid:12; - u32 s_dei:1; - u32 s_pri:3; - }; - u32 config2; - }; - - u8 alignment[16]; /* for 32 byte alignment */ -}; - - -struct switch_regs { - u32 phy_control; - u32 phy_auto_addr; - u32 mac_glob_cfg; - u32 mac_cfg[4]; - u32 mac_pri_ctrl[5], __res; - u32 etype[2]; - u32 udp_range[4]; - u32 prio_etype_udp; - u32 prio_ipdscp[8]; - u32 tc_ctrl; - u32 rate_ctrl; - u32 fc_glob_thrs; - u32 fc_port_thrs; - u32 mc_fc_glob_thrs; - u32 dc_glob_thrs; - u32 arl_vlan_cmd; - u32 arl_ctrl[3]; - u32 vlan_cfg; - u32 pvid[2]; - u32 vlan_ctrl[3]; - u32 session_id[8]; - u32 intr_stat; - u32 intr_mask; - u32 sram_test; - u32 mem_queue; - u32 farl_ctrl; - u32 fc_input_thrs, __res1[2]; - u32 clk_skew_ctrl; - u32 mac_glob_cfg_ext, __res2[2]; - u32 dma_ring_ctrl; - u32 dma_auto_poll_cfg; - u32 delay_intr_cfg, __res3; - u32 ts_dma_ctrl0; - u32 ts_desc_ptr0; - u32 ts_desc_base_addr0, __res4; - u32 fs_dma_ctrl0; - u32 fs_desc_ptr0; - u32 fs_desc_base_addr0, __res5; - u32 ts_dma_ctrl1; - u32 ts_desc_ptr1; - u32 ts_desc_base_addr1, __res6; - u32 fs_dma_ctrl1; - u32 fs_desc_ptr1; - u32 fs_desc_base_addr1; - u32 __res7[109]; - u32 mac_counter0[13]; -}; - -struct _tx_ring { - struct tx_desc *desc; - dma_addr_t phys_addr; - struct tx_desc *cur_addr; - struct sk_buff *buff_tab[TX_DESCS]; - unsigned int phys_tab[TX_DESCS]; - u32 free_index; - u32 count_index; - u32 cur_index; - int num_used; - int num_count; - bool stopped; -}; - -struct _rx_ring { - struct rx_desc *desc; - dma_addr_t phys_addr; - struct rx_desc *cur_addr; - void *buff_tab[RX_DESCS]; - unsigned int phys_tab[RX_DESCS]; - u32 cur_index; - u32 alloc_index; - int alloc_count; -}; - -struct sw { - struct switch_regs __iomem *regs; - struct napi_struct napi; - struct cns3xxx_plat_info *plat; - struct _tx_ring tx_ring; - struct _rx_ring rx_ring; - struct sk_buff *frag_first; - struct sk_buff *frag_last; - struct device *dev; - int rx_irq; - int stat_irq; -}; - -struct port { - struct net_device *netdev; - struct phy_device *phydev; - struct sw *sw; - int id; /* logical port ID */ - int speed, duplex; -}; - -static spinlock_t mdio_lock; -static DEFINE_SPINLOCK(tx_lock); -static struct switch_regs __iomem *mdio_regs; /* mdio command and status only */ -struct mii_bus *mdio_bus; -static int ports_open; -static struct port *switch_port_tab[4]; -struct net_device *napi_dev; - -static int cns3xxx_mdio_cmd(struct mii_bus *bus, int phy_id, int location, - int write, u16 cmd) -{ - int cycles = 0; - u32 temp = 0; - - temp = __raw_readl(&mdio_regs->phy_control); - temp |= MDIO_CMD_COMPLETE; - __raw_writel(temp, &mdio_regs->phy_control); - udelay(10); - - if (write) { - temp = (cmd << MDIO_VALUE_OFFSET); - temp |= MDIO_WRITE_COMMAND; - } else { - temp = MDIO_READ_COMMAND; - } - - temp |= ((location & 0x1f) << MDIO_REG_OFFSET); - temp |= (phy_id & 0x1f); - - __raw_writel(temp, &mdio_regs->phy_control); - - while (((__raw_readl(&mdio_regs->phy_control) & MDIO_CMD_COMPLETE) == 0) - && cycles < 5000) { - udelay(1); - cycles++; - } - - if (cycles == 5000) { - printk(KERN_ERR "%s #%i: MII transaction failed\n", bus->name, phy_id); - return -1; - } - - temp = __raw_readl(&mdio_regs->phy_control); - temp |= MDIO_CMD_COMPLETE; - __raw_writel(temp, &mdio_regs->phy_control); - - if (write) - return 0; - - return ((temp >> MDIO_VALUE_OFFSET) & 0xFFFF); -} - -static int cns3xxx_mdio_read(struct mii_bus *bus, int phy_id, int location) -{ - unsigned long flags; - int ret; - - spin_lock_irqsave(&mdio_lock, flags); - ret = cns3xxx_mdio_cmd(bus, phy_id, location, 0, 0); - spin_unlock_irqrestore(&mdio_lock, flags); - return ret; -} - -static int cns3xxx_mdio_write(struct mii_bus *bus, int phy_id, int location, u16 val) -{ - unsigned long flags; - int ret; - - spin_lock_irqsave(&mdio_lock, flags); - ret = cns3xxx_mdio_cmd(bus, phy_id, location, 1, val); - spin_unlock_irqrestore(&mdio_lock, flags); - return ret; -} - -static int cns3xxx_mdio_register(void __iomem *base) -{ - int err; - - if (!(mdio_bus = mdiobus_alloc())) - return -ENOMEM; - - mdio_regs = base; - - spin_lock_init(&mdio_lock); - mdio_bus->name = "CNS3xxx MII Bus"; - mdio_bus->read = &cns3xxx_mdio_read; - mdio_bus->write = &cns3xxx_mdio_write; - strcpy(mdio_bus->id, "0"); - - if ((err = mdiobus_register(mdio_bus))) - mdiobus_free(mdio_bus); - - return err; -} - -static void cns3xxx_mdio_remove(void) -{ - mdiobus_unregister(mdio_bus); - mdiobus_free(mdio_bus); -} - -static void enable_tx_dma(struct sw *sw) -{ - __raw_writel(0x1, &sw->regs->ts_dma_ctrl0); -} - -static void enable_rx_dma(struct sw *sw) -{ - __raw_writel(0x1, &sw->regs->fs_dma_ctrl0); -} - -static void cns3xxx_adjust_link(struct net_device *dev) -{ - struct port *port = netdev_priv(dev); - struct phy_device *phydev = port->phydev; - - if (!phydev->link) { - if (port->speed) { - port->speed = 0; - printk(KERN_INFO "%s: link down\n", dev->name); - } - return; - } - - if (port->speed == phydev->speed && port->duplex == phydev->duplex) - return; - - port->speed = phydev->speed; - port->duplex = phydev->duplex; - - printk(KERN_INFO "%s: link up, speed %u Mb/s, %s duplex\n", - dev->name, port->speed, port->duplex ? "full" : "half"); -} - -static void eth_schedule_poll(struct sw *sw) -{ - if (unlikely(!napi_schedule_prep(&sw->napi))) - return; - - disable_irq_nosync(sw->rx_irq); - __napi_schedule(&sw->napi); -} - -irqreturn_t eth_rx_irq(int irq, void *pdev) -{ - struct net_device *dev = pdev; - struct sw *sw = netdev_priv(dev); - eth_schedule_poll(sw); - return (IRQ_HANDLED); -} - -irqreturn_t eth_stat_irq(int irq, void *pdev) -{ - struct net_device *dev = pdev; - struct sw *sw = netdev_priv(dev); - u32 cfg; - u32 stat = __raw_readl(&sw->regs->intr_stat); - __raw_writel(0xffffffff, &sw->regs->intr_stat); - - if (stat & MAC2_RX_ERROR) - switch_port_tab[3]->netdev->stats.rx_dropped++; - if (stat & MAC1_RX_ERROR) - switch_port_tab[1]->netdev->stats.rx_dropped++; - if (stat & MAC0_RX_ERROR) - switch_port_tab[0]->netdev->stats.rx_dropped++; - - if (stat & MAC0_STATUS_CHANGE) { - cfg = __raw_readl(&sw->regs->mac_cfg[0]); - switch_port_tab[0]->phydev->link = (cfg & 0x1); - switch_port_tab[0]->phydev->duplex = ((cfg >> 4) & 0x1); - if (((cfg >> 2) & 0x3) == 2) - switch_port_tab[0]->phydev->speed = 1000; - else if (((cfg >> 2) & 0x3) == 1) - switch_port_tab[0]->phydev->speed = 100; - else - switch_port_tab[0]->phydev->speed = 10; - cns3xxx_adjust_link(switch_port_tab[0]->netdev); - } - - if (stat & MAC1_STATUS_CHANGE) { - cfg = __raw_readl(&sw->regs->mac_cfg[1]); - switch_port_tab[1]->phydev->link = (cfg & 0x1); - switch_port_tab[1]->phydev->duplex = ((cfg >> 4) & 0x1); - if (((cfg >> 2) & 0x3) == 2) - switch_port_tab[1]->phydev->speed = 1000; - else if (((cfg >> 2) & 0x3) == 1) - switch_port_tab[1]->phydev->speed = 100; - else - switch_port_tab[1]->phydev->speed = 10; - cns3xxx_adjust_link(switch_port_tab[1]->netdev); - } - - if (stat & MAC2_STATUS_CHANGE) { - cfg = __raw_readl(&sw->regs->mac_cfg[3]); - switch_port_tab[3]->phydev->link = (cfg & 0x1); - switch_port_tab[3]->phydev->duplex = ((cfg >> 4) & 0x1); - if (((cfg >> 2) & 0x3) == 2) - switch_port_tab[3]->phydev->speed = 1000; - else if (((cfg >> 2) & 0x3) == 1) - switch_port_tab[3]->phydev->speed = 100; - else - switch_port_tab[3]->phydev->speed = 10; - cns3xxx_adjust_link(switch_port_tab[3]->netdev); - } - - return (IRQ_HANDLED); -} - - -static void cns3xxx_alloc_rx_buf(struct sw *sw, int received) -{ - struct _rx_ring *rx_ring = &sw->rx_ring; - unsigned int i = rx_ring->alloc_index; - struct rx_desc *desc = &(rx_ring)->desc[i]; - void *buf; - unsigned int phys; - - for (received += rx_ring->alloc_count; received > 0; received--) { - buf = napi_alloc_frag(RX_SEGMENT_ALLOC_SIZE); - if (!buf) - break; - - phys = dma_map_single(sw->dev, buf + SKB_HEAD_ALIGN, - RX_SEGMENT_MRU, DMA_FROM_DEVICE); - if (dma_mapping_error(sw->dev, phys)) { - skb_free_frag(buf); - break; - } - - desc->sdl = RX_SEGMENT_MRU; - desc->sdp = phys; - - wmb(); - - /* put the new buffer on RX-free queue */ - rx_ring->buff_tab[i] = buf; - rx_ring->phys_tab[i] = phys; - - if (i == RX_DESCS - 1) { - desc->config0 = FIRST_SEGMENT | LAST_SEGMENT | RX_SEGMENT_MRU | END_OF_RING; - i = 0; - desc = &(rx_ring)->desc[i]; - } else { - desc->config0 = FIRST_SEGMENT | LAST_SEGMENT | RX_SEGMENT_MRU; - i++; - desc++; - } - } - - rx_ring->alloc_count = received; - rx_ring->alloc_index = i; -} - -static void eth_check_num_used(struct _tx_ring *tx_ring) -{ - bool stop = false; - int i; - - if (tx_ring->num_used >= TX_DESCS - TX_DESC_RESERVE) - stop = true; - - if (tx_ring->stopped == stop) - return; - - tx_ring->stopped = stop; - - for (i = 0; i < 4; i++) { - struct port *port = switch_port_tab[i]; - struct net_device *dev; - - if (!port) - continue; - - dev = port->netdev; - - if (stop) - netif_stop_queue(dev); - else - netif_wake_queue(dev); - } -} - -static void eth_complete_tx(struct sw *sw) -{ - struct _tx_ring *tx_ring = &sw->tx_ring; - struct tx_desc *desc; - int i; - int index; - int num_used = tx_ring->num_used; - struct sk_buff *skb; - - index = tx_ring->free_index; - desc = &(tx_ring)->desc[index]; - - for (i = 0; i < num_used; i++) { - if (!desc->cown) - break; - - skb = tx_ring->buff_tab[index]; - tx_ring->buff_tab[index] = 0; - - if (skb) - dev_kfree_skb_any(skb); - - dma_unmap_single(sw->dev, tx_ring->phys_tab[index], desc->sdl, DMA_TO_DEVICE); - - if (index == TX_DESCS - 1) { - index = 0; - desc = &(tx_ring)->desc[index]; - } else { - index++; - desc++; - } - } - - tx_ring->free_index = index; - tx_ring->num_used -= i; - eth_check_num_used(tx_ring); -} - -static int eth_poll(struct napi_struct *napi, int budget) -{ - struct sw *sw = container_of(napi, struct sw, napi); - struct _rx_ring *rx_ring = &sw->rx_ring; - int received = 0; - unsigned int length; - unsigned int i = rx_ring->cur_index; - struct rx_desc *desc = &(rx_ring)->desc[i]; - unsigned int alloc_count = rx_ring->alloc_count; - - while (desc->cown && alloc_count + received < RX_DESCS - 1) { - struct sk_buff *skb; - int reserve = SKB_HEAD_ALIGN; - - if (received >= budget) - break; - - /* process received frame */ - dma_unmap_single(sw->dev, rx_ring->phys_tab[i], RX_SEGMENT_MRU, DMA_FROM_DEVICE); - - skb = build_skb(rx_ring->buff_tab[i], RX_SEGMENT_ALLOC_SIZE); - if (!skb) - break; - - skb->dev = switch_port_tab[desc->sp]->netdev; - - length = desc->sdl; - if (desc->fsd && !desc->lsd) - length = RX_SEGMENT_MRU; - - if (!desc->fsd) { - reserve -= NET_IP_ALIGN; - if (!desc->lsd) - length += NET_IP_ALIGN; - } - - skb_reserve(skb, reserve); - skb_put(skb, length); - - if (!sw->frag_first) - sw->frag_first = skb; - else { - if (sw->frag_first == sw->frag_last) - skb_shinfo(sw->frag_first)->frag_list = skb; - else - sw->frag_last->next = skb; - sw->frag_first->len += skb->len; - sw->frag_first->data_len += skb->len; - sw->frag_first->truesize += skb->truesize; - } - sw->frag_last = skb; - - if (desc->lsd) { - struct net_device *dev; - - skb = sw->frag_first; - dev = skb->dev; - skb->protocol = eth_type_trans(skb, dev); - - dev->stats.rx_packets++; - dev->stats.rx_bytes += skb->len; - - /* RX Hardware checksum offload */ - skb->ip_summed = CHECKSUM_NONE; - switch (desc->prot) { - case 1: - case 2: - case 5: - case 6: - case 13: - case 14: - if (!desc->l4f) { - skb->ip_summed = CHECKSUM_UNNECESSARY; - napi_gro_receive(napi, skb); - break; - } - /* fall through */ - default: - netif_receive_skb(skb); - break; - } - - sw->frag_first = NULL; - sw->frag_last = NULL; - } - - received++; - if (i == RX_DESCS - 1) { - i = 0; - desc = &(rx_ring)->desc[i]; - } else { - i++; - desc++; - } - } - - rx_ring->cur_index = i; - - cns3xxx_alloc_rx_buf(sw, received); - wmb(); - enable_rx_dma(sw); - - if (received < budget && napi_complete_done(napi, received)) { - enable_irq(sw->rx_irq); - } - - spin_lock_bh(&tx_lock); - eth_complete_tx(sw); - spin_unlock_bh(&tx_lock); - - return received; -} - -static void eth_set_desc(struct sw *sw, struct _tx_ring *tx_ring, int index, - int index_last, void *data, int len, u32 config0, - u32 pmap) -{ - struct tx_desc *tx_desc = &(tx_ring)->desc[index]; - unsigned int phys; - - phys = dma_map_single(sw->dev, data, len, DMA_TO_DEVICE); - tx_desc->sdp = phys; - tx_desc->pmap = pmap; - tx_ring->phys_tab[index] = phys; - - config0 |= len; - - if (index == TX_DESCS - 1) - config0 |= END_OF_RING; - - if (index == index_last) - config0 |= LAST_SEGMENT; - - wmb(); - tx_desc->config0 = config0; -} - -static int eth_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct port *port = netdev_priv(dev); - struct sw *sw = port->sw; - struct _tx_ring *tx_ring = &sw->tx_ring; - struct sk_buff *skb1; - char pmap = (1 << port->id); - int nr_frags = skb_shinfo(skb)->nr_frags; - int nr_desc = nr_frags; - int index0, index, index_last; - int len0; - int i; - u32 config0; - - if (pmap == 8) - pmap = (1 << 4); - - skb_walk_frags(skb, skb1) - nr_desc++; - - eth_schedule_poll(sw); - spin_lock_bh(&tx_lock); - - if ((tx_ring->num_used + nr_desc + 1) >= TX_DESCS) { - spin_unlock_bh(&tx_lock); - return NETDEV_TX_BUSY; - } - - index = index0 = tx_ring->cur_index; - index_last = (index0 + nr_desc) % TX_DESCS; - tx_ring->cur_index = (index_last + 1) % TX_DESCS; - - spin_unlock_bh(&tx_lock); - - config0 = FORCE_ROUTE; - if (skb->ip_summed == CHECKSUM_PARTIAL) - config0 |= UDP_CHECKSUM | TCP_CHECKSUM; - - len0 = skb->len; - - /* fragments */ - for (i = 0; i < nr_frags; i++) { - struct skb_frag_struct *frag; - void *addr; - - index = (index + 1) % TX_DESCS; - - frag = &skb_shinfo(skb)->frags[i]; - addr = page_address(skb_frag_page(frag)) + frag->page_offset; - - eth_set_desc(sw, tx_ring, index, index_last, addr, frag->size, - config0, pmap); - } - - if (nr_frags) - len0 = skb->len - skb->data_len; - - skb_walk_frags(skb, skb1) { - index = (index + 1) % TX_DESCS; - len0 -= skb1->len; - - eth_set_desc(sw, tx_ring, index, index_last, skb1->data, - skb1->len, config0, pmap); - } - - tx_ring->buff_tab[index0] = skb; - eth_set_desc(sw, tx_ring, index0, index_last, skb->data, len0, - config0 | FIRST_SEGMENT, pmap); - - wmb(); - - spin_lock(&tx_lock); - tx_ring->num_used += nr_desc + 1; - spin_unlock(&tx_lock); - - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - - enable_tx_dma(sw); - - return NETDEV_TX_OK; -} - -static int eth_ioctl(struct net_device *dev, struct ifreq *req, int cmd) -{ - struct port *port = netdev_priv(dev); - - if (!netif_running(dev)) - return -EINVAL; - return phy_mii_ioctl(port->phydev, req, cmd); -} - -/* ethtool support */ - -static void cns3xxx_get_drvinfo(struct net_device *dev, - struct ethtool_drvinfo *info) -{ - strcpy(info->driver, DRV_NAME); - strcpy(info->bus_info, "internal"); -} - -static int cns3xxx_nway_reset(struct net_device *dev) -{ - struct port *port = netdev_priv(dev); - return phy_start_aneg(port->phydev); -} - -static struct ethtool_ops cns3xxx_ethtool_ops = { - .get_drvinfo = cns3xxx_get_drvinfo, - .get_link_ksettings = phy_ethtool_get_link_ksettings, - .set_link_ksettings = phy_ethtool_set_link_ksettings, - .nway_reset = cns3xxx_nway_reset, - .get_link = ethtool_op_get_link, -}; - - -static int init_rings(struct sw *sw) -{ - int i; - struct _rx_ring *rx_ring = &sw->rx_ring; - struct _tx_ring *tx_ring = &sw->tx_ring; - - __raw_writel(0, &sw->regs->fs_dma_ctrl0); - __raw_writel(TS_SUSPEND | FS_SUSPEND, &sw->regs->dma_auto_poll_cfg); - __raw_writel(QUEUE_THRESHOLD, &sw->regs->dma_ring_ctrl); - __raw_writel(CLR_FS_STATE | QUEUE_THRESHOLD, &sw->regs->dma_ring_ctrl); - __raw_writel(QUEUE_THRESHOLD, &sw->regs->dma_ring_ctrl); - - rx_ring->desc = dmam_alloc_coherent(sw->dev, RX_POOL_ALLOC_SIZE, - &rx_ring->phys_addr, GFP_KERNEL); - if (!rx_ring->desc) - return -ENOMEM; - - /* Setup RX buffers */ - memset(rx_ring->desc, 0, RX_POOL_ALLOC_SIZE); - - for (i = 0; i < RX_DESCS; i++) { - struct rx_desc *desc = &(rx_ring)->desc[i]; - void *buf; - - buf = netdev_alloc_frag(RX_SEGMENT_ALLOC_SIZE); - if (!buf) - return -ENOMEM; - - desc->sdl = RX_SEGMENT_MRU; - - if (i == (RX_DESCS - 1)) - desc->eor = 1; - - desc->fsd = 1; - desc->lsd = 1; - - desc->sdp = dma_map_single(sw->dev, buf + SKB_HEAD_ALIGN, - RX_SEGMENT_MRU, DMA_FROM_DEVICE); - - if (dma_mapping_error(sw->dev, desc->sdp)) - return -EIO; - - rx_ring->buff_tab[i] = buf; - rx_ring->phys_tab[i] = desc->sdp; - desc->cown = 0; - } - __raw_writel(rx_ring->phys_addr, &sw->regs->fs_desc_ptr0); - __raw_writel(rx_ring->phys_addr, &sw->regs->fs_desc_base_addr0); - - tx_ring->desc = dmam_alloc_coherent(sw->dev, TX_POOL_ALLOC_SIZE, - &tx_ring->phys_addr, GFP_KERNEL); - if (!tx_ring->desc) - return -ENOMEM; - - /* Setup TX buffers */ - memset(tx_ring->desc, 0, TX_POOL_ALLOC_SIZE); - - for (i = 0; i < TX_DESCS; i++) { - struct tx_desc *desc = &(tx_ring)->desc[i]; - tx_ring->buff_tab[i] = 0; - - if (i == (TX_DESCS - 1)) - desc->eor = 1; - - desc->cown = 1; - } - __raw_writel(tx_ring->phys_addr, &sw->regs->ts_desc_ptr0); - __raw_writel(tx_ring->phys_addr, &sw->regs->ts_desc_base_addr0); - - return 0; -} - -static void destroy_rings(struct sw *sw) -{ - int i; - - for (i = 0; i < RX_DESCS; i++) { - struct _rx_ring *rx_ring = &sw->rx_ring; - struct rx_desc *desc = &(rx_ring)->desc[i]; - void *buf = sw->rx_ring.buff_tab[i]; - - if (!buf) - continue; - - dma_unmap_single(sw->dev, desc->sdp, RX_SEGMENT_MRU, DMA_FROM_DEVICE); - skb_free_frag(buf); - } - - for (i = 0; i < TX_DESCS; i++) { - struct _tx_ring *tx_ring = &sw->tx_ring; - struct tx_desc *desc = &(tx_ring)->desc[i]; - struct sk_buff *skb = sw->tx_ring.buff_tab[i]; - - if (!skb) - continue; - - dma_unmap_single(sw->dev, desc->sdp, skb->len, DMA_TO_DEVICE); - dev_kfree_skb(skb); - } -} - -static int eth_open(struct net_device *dev) -{ - struct port *port = netdev_priv(dev); - struct sw *sw = port->sw; - u32 temp; - - port->speed = 0; /* force "link up" message */ - phy_start(port->phydev); - - netif_start_queue(dev); - - if (!ports_open) { - request_irq(sw->rx_irq, eth_rx_irq, IRQF_SHARED, "gig_switch", napi_dev); - request_irq(sw->stat_irq, eth_stat_irq, IRQF_SHARED, "gig_stat", napi_dev); - napi_enable(&sw->napi); - netif_start_queue(napi_dev); - - __raw_writel(~(MAC0_STATUS_CHANGE | MAC1_STATUS_CHANGE | MAC2_STATUS_CHANGE | - MAC0_RX_ERROR | MAC1_RX_ERROR | MAC2_RX_ERROR), &sw->regs->intr_mask); - - temp = __raw_readl(&sw->regs->mac_cfg[2]); - temp &= ~(PORT_DISABLE); - __raw_writel(temp, &sw->regs->mac_cfg[2]); - - temp = __raw_readl(&sw->regs->dma_auto_poll_cfg); - temp &= ~(TS_SUSPEND | FS_SUSPEND); - __raw_writel(temp, &sw->regs->dma_auto_poll_cfg); - - enable_rx_dma(sw); - } - temp = __raw_readl(&sw->regs->mac_cfg[port->id]); - temp &= ~(PORT_DISABLE); - __raw_writel(temp, &sw->regs->mac_cfg[port->id]); - - ports_open++; - netif_carrier_on(dev); - - return 0; -} - -static int eth_close(struct net_device *dev) -{ - struct port *port = netdev_priv(dev); - struct sw *sw = port->sw; - u32 temp; - - ports_open--; - - temp = __raw_readl(&sw->regs->mac_cfg[port->id]); - temp |= (PORT_DISABLE); - __raw_writel(temp, &sw->regs->mac_cfg[port->id]); - - netif_stop_queue(dev); - - phy_stop(port->phydev); - - if (!ports_open) { - disable_irq(sw->rx_irq); - free_irq(sw->rx_irq, napi_dev); - disable_irq(sw->stat_irq); - free_irq(sw->stat_irq, napi_dev); - napi_disable(&sw->napi); - netif_stop_queue(napi_dev); - temp = __raw_readl(&sw->regs->mac_cfg[2]); - temp |= (PORT_DISABLE); - __raw_writel(temp, &sw->regs->mac_cfg[2]); - - __raw_writel(TS_SUSPEND | FS_SUSPEND, - &sw->regs->dma_auto_poll_cfg); - } - - netif_carrier_off(dev); - return 0; -} - -static void eth_rx_mode(struct net_device *dev) -{ - struct port *port = netdev_priv(dev); - struct sw *sw = port->sw; - u32 temp; - - temp = __raw_readl(&sw->regs->mac_glob_cfg); - - if (dev->flags & IFF_PROMISC) { - if (port->id == 3) - temp |= ((1 << 2) << PROMISC_OFFSET); - else - temp |= ((1 << port->id) << PROMISC_OFFSET); - } else { - if (port->id == 3) - temp &= ~((1 << 2) << PROMISC_OFFSET); - else - temp &= ~((1 << port->id) << PROMISC_OFFSET); - } - __raw_writel(temp, &sw->regs->mac_glob_cfg); -} - -static int eth_set_mac(struct net_device *netdev, void *p) -{ - struct port *port = netdev_priv(netdev); - struct sw *sw = port->sw; - struct sockaddr *addr = p; - u32 cycles = 0; - - if (!is_valid_ether_addr(addr->sa_data)) - return -EADDRNOTAVAIL; - - /* Invalidate old ARL Entry */ - if (port->id == 3) - __raw_writel((port->id << 16) | (0x4 << 9), &sw->regs->arl_ctrl[0]); - else - __raw_writel(((port->id + 1) << 16) | (0x4 << 9), &sw->regs->arl_ctrl[0]); - __raw_writel( ((netdev->dev_addr[0] << 24) | (netdev->dev_addr[1] << 16) | - (netdev->dev_addr[2] << 8) | (netdev->dev_addr[3])), - &sw->regs->arl_ctrl[1]); - - __raw_writel( ((netdev->dev_addr[4] << 24) | (netdev->dev_addr[5] << 16) | - (1 << 1)), - &sw->regs->arl_ctrl[2]); - __raw_writel((1 << 19), &sw->regs->arl_vlan_cmd); - - while (((__raw_readl(&sw->regs->arl_vlan_cmd) & (1 << 21)) == 0) - && cycles < 5000) { - udelay(1); - cycles++; - } - - cycles = 0; - memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); - - if (port->id == 3) - __raw_writel((port->id << 16) | (0x4 << 9), &sw->regs->arl_ctrl[0]); - else - __raw_writel(((port->id + 1) << 16) | (0x4 << 9), &sw->regs->arl_ctrl[0]); - __raw_writel( ((addr->sa_data[0] << 24) | (addr->sa_data[1] << 16) | - (addr->sa_data[2] << 8) | (addr->sa_data[3])), - &sw->regs->arl_ctrl[1]); - - __raw_writel( ((addr->sa_data[4] << 24) | (addr->sa_data[5] << 16) | - (7 << 4) | (1 << 1)), &sw->regs->arl_ctrl[2]); - __raw_writel((1 << 19), &sw->regs->arl_vlan_cmd); - - while (((__raw_readl(&sw->regs->arl_vlan_cmd) & (1 << 21)) == 0) - && cycles < 5000) { - udelay(1); - cycles++; - } - return 0; -} - -static const struct net_device_ops cns3xxx_netdev_ops = { - .ndo_open = eth_open, - .ndo_stop = eth_close, - .ndo_start_xmit = eth_xmit, - .ndo_set_rx_mode = eth_rx_mode, - .ndo_do_ioctl = eth_ioctl, - .ndo_set_mac_address = eth_set_mac, - .ndo_validate_addr = eth_validate_addr, -}; - -static int eth_init_one(struct platform_device *pdev) -{ - int i; - struct port *port; - struct sw *sw; - struct net_device *dev; - struct cns3xxx_plat_info *plat = pdev->dev.platform_data; - char phy_id[MII_BUS_ID_SIZE + 3]; - int err; - u32 temp; - struct resource *res; - void __iomem *regs; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - regs = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(regs)) - return PTR_ERR(regs); - - err = cns3xxx_mdio_register(regs); - if (err) - return err; - - if (!(napi_dev = alloc_etherdev(sizeof(struct sw)))) { - err = -ENOMEM; - goto err_remove_mdio; - } - - strcpy(napi_dev->name, "cns3xxx_eth"); - napi_dev->features = NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_FRAGLIST; - - SET_NETDEV_DEV(napi_dev, &pdev->dev); - sw = netdev_priv(napi_dev); - memset(sw, 0, sizeof(struct sw)); - sw->regs = regs; - sw->dev = &pdev->dev; - - sw->rx_irq = platform_get_irq_byname(pdev, "eth_rx"); - sw->stat_irq = platform_get_irq_byname(pdev, "eth_stat"); - - temp = __raw_readl(&sw->regs->phy_auto_addr); - temp |= (3 << 30); /* maximum frame length: 9600 bytes */ - __raw_writel(temp, &sw->regs->phy_auto_addr); - - for (i = 0; i < 4; i++) { - temp = __raw_readl(&sw->regs->mac_cfg[i]); - temp |= (PORT_DISABLE); - __raw_writel(temp, &sw->regs->mac_cfg[i]); - } - - temp = PORT_DISABLE; - __raw_writel(temp, &sw->regs->mac_cfg[2]); - - temp = __raw_readl(&sw->regs->vlan_cfg); - temp |= NIC_MODE | VLAN_UNAWARE; - __raw_writel(temp, &sw->regs->vlan_cfg); - - __raw_writel(UNKNOWN_VLAN_TO_CPU | - CRC_STRIPPING, &sw->regs->mac_glob_cfg); - - if ((err = init_rings(sw)) != 0) { - err = -ENOMEM; - goto err_free; - } - platform_set_drvdata(pdev, napi_dev); - - netif_napi_add(napi_dev, &sw->napi, eth_poll, NAPI_WEIGHT); - - for (i = 0; i < 3; i++) { - if (!(plat->ports & (1 << i))) { - continue; - } - - if (!(dev = alloc_etherdev(sizeof(struct port)))) { - goto free_ports; - } - - port = netdev_priv(dev); - port->netdev = dev; - if (i == 2) - port->id = 3; - else - port->id = i; - port->sw = sw; - - temp = __raw_readl(&sw->regs->mac_cfg[port->id]); - temp |= (PORT_DISABLE | PORT_BLOCK_STATE | PORT_LEARN_DIS); - __raw_writel(temp, &sw->regs->mac_cfg[port->id]); - - SET_NETDEV_DEV(dev, &pdev->dev); - dev->netdev_ops = &cns3xxx_netdev_ops; - dev->ethtool_ops = &cns3xxx_ethtool_ops; - dev->tx_queue_len = 1000; - dev->max_mtu = MAX_MTU; - dev->features = NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_FRAGLIST; - - switch_port_tab[port->id] = port; - memcpy(dev->dev_addr, &plat->hwaddr[i], ETH_ALEN); - - snprintf(phy_id, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, "0", plat->phy[i]); - port->phydev = phy_connect(dev, phy_id, &cns3xxx_adjust_link, - PHY_INTERFACE_MODE_RGMII); - if ((err = IS_ERR(port->phydev))) { - switch_port_tab[port->id] = 0; - free_netdev(dev); - goto free_ports; - } - - port->phydev->irq = PHY_IGNORE_INTERRUPT; - - if ((err = register_netdev(dev))) { - phy_disconnect(port->phydev); - switch_port_tab[port->id] = 0; - free_netdev(dev); - goto free_ports; - } - - printk(KERN_INFO "%s: RGMII PHY %i on cns3xxx Switch\n", dev->name, plat->phy[i]); - netif_carrier_off(dev); - dev = 0; - } - - return 0; - -free_ports: - err = -ENOMEM; - for (--i; i >= 0; i--) { - if (switch_port_tab[i]) { - port = switch_port_tab[i]; - dev = port->netdev; - unregister_netdev(dev); - phy_disconnect(port->phydev); - switch_port_tab[i] = 0; - free_netdev(dev); - } - } -err_free: - free_netdev(napi_dev); -err_remove_mdio: - cns3xxx_mdio_remove(); - return err; -} - -static int eth_remove_one(struct platform_device *pdev) -{ - struct net_device *dev = platform_get_drvdata(pdev); - struct sw *sw = netdev_priv(dev); - int i; - - destroy_rings(sw); - for (i = 3; i >= 0; i--) { - if (switch_port_tab[i]) { - struct port *port = switch_port_tab[i]; - struct net_device *dev = port->netdev; - unregister_netdev(dev); - phy_disconnect(port->phydev); - switch_port_tab[i] = 0; - free_netdev(dev); - } - } - - free_netdev(napi_dev); - cns3xxx_mdio_remove(); - - return 0; -} - -static struct platform_driver cns3xxx_eth_driver = { - .driver.name = DRV_NAME, - .probe = eth_init_one, - .remove = eth_remove_one, -}; - -static int __init eth_init_module(void) -{ - return platform_driver_register(&cns3xxx_eth_driver); -} - -static void __exit eth_cleanup_module(void) -{ - platform_driver_unregister(&cns3xxx_eth_driver); -} - -module_init(eth_init_module); -module_exit(eth_cleanup_module); - -MODULE_AUTHOR("Chris Lang"); -MODULE_DESCRIPTION("Cavium CNS3xxx Ethernet driver"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:cns3xxx_eth"); diff --git a/target/linux/cns3xxx/files/drivers/spi/spi-cns3xxx.c b/target/linux/cns3xxx/files/drivers/spi/spi-cns3xxx.c deleted file mode 100644 index 63019862ee..0000000000 --- a/target/linux/cns3xxx/files/drivers/spi/spi-cns3xxx.c +++ /dev/null @@ -1,448 +0,0 @@ -/******************************************************************************* - * - * CNS3XXX SPI controller driver (master mode only) - * - * Copyright (c) 2008 Cavium Networks - * Copyright 2011 Gateworks Corporation - * Chris Lang - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, Version 2, as - * published by the Free Software Foundation. - * - * This file is distributed in the hope that it will be useful, - * but AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or - * NONINFRINGEMENT. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this file; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA or - * visit http://www.gnu.org/licenses/. - * - * This file may also be available under a different license from Cavium. - * Contact Cavium Networks for more information - * - ******************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -/* - * define access macros - */ -#define SPI_MEM_MAP_VALUE(reg_offset) (*((u32 volatile *)(hw->base + reg_offset))) - -#define SPI_CONFIGURATION_REG SPI_MEM_MAP_VALUE(0x00) -#define SPI_SERVICE_STATUS_REG SPI_MEM_MAP_VALUE(0x04) -#define SPI_BIT_RATE_CONTROL_REG SPI_MEM_MAP_VALUE(0x08) -#define SPI_TRANSMIT_CONTROL_REG SPI_MEM_MAP_VALUE(0x0C) -#define SPI_TRANSMIT_BUFFER_REG SPI_MEM_MAP_VALUE(0x10) -#define SPI_RECEIVE_CONTROL_REG SPI_MEM_MAP_VALUE(0x14) -#define SPI_RECEIVE_BUFFER_REG SPI_MEM_MAP_VALUE(0x18) -#define SPI_FIFO_TRANSMIT_CONFIG_REG SPI_MEM_MAP_VALUE(0x1C) -#define SPI_FIFO_TRANSMIT_CONTROL_REG SPI_MEM_MAP_VALUE(0x20) -#define SPI_FIFO_RECEIVE_CONFIG_REG SPI_MEM_MAP_VALUE(0x24) -#define SPI_INTERRUPT_STATUS_REG SPI_MEM_MAP_VALUE(0x28) -#define SPI_INTERRUPT_ENABLE_REG SPI_MEM_MAP_VALUE(0x2C) - -#define SPI_TRANSMIT_BUFFER_REG_ADDR (CNS3XXX_SSP_BASE +0x10) -#define SPI_RECEIVE_BUFFER_REG_ADDR (CNS3XXX_SSP_BASE +0x18) - -/* Structure for SPI controller of CNS3XXX SOCs */ -struct cns3xxx_spi { - /* bitbang has to be first */ - struct spi_bitbang bitbang; - struct completion done; - wait_queue_head_t wait; - - int len; - int count; - int last_in_message_list; - - /* data buffers */ - const unsigned char *tx; - unsigned char *rx; - - void __iomem *base; - struct spi_master *master; - struct platform_device *pdev; - struct device *dev; -}; - -static inline u8 cns3xxx_spi_bus_idle(struct cns3xxx_spi *hw) -{ - return ((SPI_SERVICE_STATUS_REG & 0x1) ? 0 : 1); -} - -static inline u8 cns3xxx_spi_tx_buffer_empty(struct cns3xxx_spi *hw) -{ - return ((SPI_INTERRUPT_STATUS_REG & (0x1 << 3)) ? 1 : 0); -} - -static inline u8 cns3xxx_spi_rx_buffer_full(struct cns3xxx_spi *hw) -{ - return ((SPI_INTERRUPT_STATUS_REG & (0x1 << 2)) ? 1 : 0); -} - -u8 cns3xxx_spi_tx_rx(struct cns3xxx_spi *hw, u8 tx_channel, u8 tx_eof, - u32 tx_data, u32 * rx_data) -{ - u8 rx_channel; - u8 rx_eof; - - while (!cns3xxx_spi_bus_idle(hw)) ; // do nothing - - while (!cns3xxx_spi_tx_buffer_empty(hw)) ; // do nothing - - SPI_TRANSMIT_CONTROL_REG &= ~(0x7); - SPI_TRANSMIT_CONTROL_REG |= (tx_channel & 0x3) | ((tx_eof & 0x1) << 2); - - SPI_TRANSMIT_BUFFER_REG = tx_data; - - while (!cns3xxx_spi_rx_buffer_full(hw)) ; // do nothing - - rx_channel = SPI_RECEIVE_CONTROL_REG & 0x3; - rx_eof = (SPI_RECEIVE_CONTROL_REG & (0x1 << 2)) ? 1 : 0; - - *rx_data = SPI_RECEIVE_BUFFER_REG; - - if ((tx_channel != rx_channel) || (tx_eof != rx_eof)) { - return 0; - } else { - return 1; - } -} - -u8 cns3xxx_spi_tx(struct cns3xxx_spi *hw, u8 tx_channel, u8 tx_eof, u32 tx_data) -{ - - while (!cns3xxx_spi_bus_idle(hw)) ; // do nothing - - while (!cns3xxx_spi_tx_buffer_empty(hw)) ; // do nothing - - SPI_TRANSMIT_CONTROL_REG &= ~(0x7); - SPI_TRANSMIT_CONTROL_REG |= (tx_channel & 0x3) | ((tx_eof & 0x1) << 2); - - SPI_TRANSMIT_BUFFER_REG = tx_data; - - return 1; -} - -static inline struct cns3xxx_spi *to_hw(struct spi_device *sdev) -{ - return spi_master_get_devdata(sdev->master); -} - -static int cns3xxx_spi_setup_transfer(struct spi_device *spi, - struct spi_transfer *t) -{ - return 0; -} - -static void cns3xxx_spi_chipselect(struct spi_device *spi, int value) -{ - struct cns3xxx_spi *hw = to_hw(spi); - unsigned int spi_config; - - switch (value) { - case BITBANG_CS_INACTIVE: - break; - - case BITBANG_CS_ACTIVE: - spi_config = SPI_CONFIGURATION_REG; - - if (spi->mode & SPI_CPHA) - spi_config |= (0x1 << 13); - else - spi_config &= ~(0x1 << 13); - - if (spi->mode & SPI_CPOL) - spi_config |= (0x1 << 14); - else - spi_config &= ~(0x1 << 14); - - /* write new configration */ - SPI_CONFIGURATION_REG = spi_config; - - SPI_TRANSMIT_CONTROL_REG &= ~(0x7); - SPI_TRANSMIT_CONTROL_REG |= (spi->chip_select & 0x3); - - break; - } -} - -static int cns3xxx_spi_setup(struct spi_device *spi) -{ - if (!spi->bits_per_word) - spi->bits_per_word = 8; - - return 0; -} - -static int cns3xxx_spi_txrx(struct spi_device *spi, struct spi_transfer *t) -{ - struct cns3xxx_spi *hw = to_hw(spi); - - dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n", t->tx_buf, t->rx_buf, - t->len); - - hw->tx = t->tx_buf; - hw->rx = t->rx_buf; - hw->len = t->len; - hw->count = 0; - hw->last_in_message_list = t->last_in_message_list; - - init_completion(&hw->done); - - if (hw->tx) { - int i; - u32 rx_data; - for (i = 0; i < (hw->len - 1); i++) { - dev_dbg(&spi->dev, - "[SPI_CNS3XXX_DEBUG] hw->tx[%02d]: 0x%02x\n", i, - hw->tx[i]); - cns3xxx_spi_tx_rx(hw, spi->chip_select, 0, hw->tx[i], - &rx_data); - if (hw->rx) { - hw->rx[i] = rx_data; - dev_dbg(&spi->dev, - "[SPI_CNS3XXX_DEBUG] hw->rx[%02d]: 0x%02x\n", - i, hw->rx[i]); - } - } - - if (t->last_in_message_list) { - cns3xxx_spi_tx_rx(hw, spi->chip_select, 1, hw->tx[i], - &rx_data); - if (hw->rx) { - hw->rx[i] = rx_data; - dev_dbg(&spi->dev, - "[SPI_CNS3XXX_DEBUG] hw->rx[%02d]: 0x%02x\n", - i, hw->rx[i]); - } - } else { - cns3xxx_spi_tx_rx(hw, spi->chip_select, 0, hw->tx[i], - &rx_data); - } - goto done; - } - - if (hw->rx) { - int i; - u32 rx_data; - for (i = 0; i < (hw->len - 1); i++) { - cns3xxx_spi_tx_rx(hw, spi->chip_select, 0, 0xff, &rx_data); - hw->rx[i] = rx_data; - dev_dbg(&spi->dev, - "[SPI_CNS3XXX_DEBUG] hw->rx[%02d]: 0x%02x\n", i, - hw->rx[i]); - } - - if (t->last_in_message_list) { - cns3xxx_spi_tx_rx(hw, spi->chip_select, 1, 0xff, &rx_data); - } else { - cns3xxx_spi_tx_rx(hw, spi->chip_select, 0, 0xff, &rx_data); - } - hw->rx[i] = rx_data; - dev_dbg(&spi->dev, "[SPI_CNS3XXX_DEBUG] hw->rx[%02d]: 0x%02x\n", - i, hw->rx[i]); - } -done: - return hw->len; -} - -static void __init cns3xxx_spi_initial(struct cns3xxx_spi *hw) -{ - SPI_CONFIGURATION_REG = (((0x0 & 0x3) << 0) | /* 8bits shift length */ - (0x0 << 9) | /* SPI mode */ - (0x0 << 10) | /* disable FIFO */ - (0x1 << 11) | /* SPI master mode */ - (0x0 << 12) | /* disable SPI loopback mode */ - (0x1 << 13) | /* clock phase */ - (0x1 << 14) | /* clock polarity */ - (0x0 << 24) | /* disable - SPI data swap */ - (0x1 << 29) | /* enable - 2IO Read mode */ - (0x0 << 30) | /* disable - SPI high speed read for system boot up */ - (0x0 << 31)); /* disable - SPI */ - - /* Set SPI bit rate PCLK/2 */ - SPI_BIT_RATE_CONTROL_REG = 0x1; - - /* Set SPI Tx channel 0 */ - SPI_TRANSMIT_CONTROL_REG = 0x0; - - /* Set Tx FIFO Threshold, Tx FIFO has 2 words */ - SPI_FIFO_TRANSMIT_CONFIG_REG &= ~(0x03 << 4); - SPI_FIFO_TRANSMIT_CONFIG_REG |= ((0x0 & 0x03) << 4); - - /* Set Rx FIFO Threshold, Rx FIFO has 2 words */ - SPI_FIFO_RECEIVE_CONFIG_REG &= ~(0x03 << 4); - SPI_FIFO_RECEIVE_CONFIG_REG |= ((0x0 & 0x03) << 4); - - /* Disable all interrupt */ - SPI_INTERRUPT_ENABLE_REG = 0x0; - - /* Clear spurious interrupt sources */ - SPI_INTERRUPT_STATUS_REG = (0x0F << 4); - - /* Enable SPI */ - SPI_CONFIGURATION_REG |= (0x1 << 31); - - return; -} - -static int cns3xxx_spi_probe(struct platform_device *pdev) -{ - struct spi_master *master; - struct cns3xxx_spi *hw; - struct resource *res; - int err = 0; - - printk("%s: setup CNS3XXX SPI Controller\n", __FUNCTION__); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; - - /* Allocate master with space for cns3xxx_spi */ - master = spi_alloc_master(&pdev->dev, sizeof(struct cns3xxx_spi)); - if (master == NULL) { - dev_err(&pdev->dev, "No memory for spi_master\n"); - err = -ENOMEM; - goto err_nomem; - } - - hw = spi_master_get_devdata(master); - memset(hw, 0, sizeof(struct cns3xxx_spi)); - - hw->master = spi_master_get(master); - hw->dev = &pdev->dev; - - hw->base = devm_ioremap_resource(hw->dev, res); - if (IS_ERR(hw->base)) { - dev_err(hw->dev, "Unable to map registers\n"); - err = PTR_ERR(hw->base); - goto err_register; - } - - platform_set_drvdata(pdev, hw); - init_completion(&hw->done); - - /* setup the master state. */ - - master->num_chipselect = 4; - master->bus_num = 1; - - /* setup the state for the bitbang driver */ - - hw->bitbang.master = hw->master; - hw->bitbang.setup_transfer = cns3xxx_spi_setup_transfer; - hw->bitbang.chipselect = cns3xxx_spi_chipselect; - hw->bitbang.txrx_bufs = cns3xxx_spi_txrx; - hw->bitbang.master->setup = cns3xxx_spi_setup; - - dev_dbg(hw->dev, "bitbang at %p\n", &hw->bitbang); - - /* SPI controller initializations */ - cns3xxx_spi_initial(hw); - - /* register SPI controller */ - - err = spi_bitbang_start(&hw->bitbang); - if (err) { - dev_err(&pdev->dev, "Failed to register SPI master\n"); - goto err_register; - } - - return 0; - -err_register: - spi_master_put(hw->master);; - -err_nomem: - return err; -} - -static int cns3xxx_spi_remove(struct platform_device *dev) -{ - struct cns3xxx_spi *hw = platform_get_drvdata(dev); - - platform_set_drvdata(dev, NULL); - - spi_unregister_master(hw->master); - - spi_master_put(hw->master); - return 0; -} - -#ifdef CONFIG_PM - -static int cns3xxx_spi_suspend(struct platform_device *pdev, pm_message_t msg) -{ - struct cns3xxx_spi *hw = platform_get_drvdata(pdev); - - return 0; -} - -static int cns3xxx_spi_resume(struct platform_device *pdev) -{ - struct cns3xxx_spi *hw = platform_get_drvdata(pdev); - - return 0; -} - -#else -#define cns3xxx_spi_suspend NULL -#define cns3xxx_spi_resume NULL -#endif - -static struct platform_driver cns3xxx_spi_driver = { - .probe = cns3xxx_spi_probe, - .remove = cns3xxx_spi_remove, - .suspend = cns3xxx_spi_suspend, - .resume = cns3xxx_spi_resume, - .driver = { - .name = "cns3xxx_spi", - .owner = THIS_MODULE, - }, -}; - -static int __init cns3xxx_spi_init(void) -{ - return platform_driver_register(&cns3xxx_spi_driver); -} - -static void __exit cns3xxx_spi_exit(void) -{ - platform_driver_unregister(&cns3xxx_spi_driver); -} - -module_init(cns3xxx_spi_init); -module_exit(cns3xxx_spi_exit); - -MODULE_AUTHOR("Cavium Networks"); -MODULE_DESCRIPTION("CNS3XXX SPI Controller Driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:cns3xxx_spi"); - -EXPORT_SYMBOL_GPL(cns3xxx_spi_tx_rx); diff --git a/target/linux/cns3xxx/files/include/linux/platform_data/cns3xxx.h b/target/linux/cns3xxx/files/include/linux/platform_data/cns3xxx.h deleted file mode 100644 index f286d0dfe9..0000000000 --- a/target/linux/cns3xxx/files/include/linux/platform_data/cns3xxx.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * arch/arm/mach-cns3xxx/include/mach/platform.h - * - * Copyright 2011 Gateworks Corporation - * Chris Lang $(BIN_DIR)/$(IMG_PREFIX)-$(1)-sysupgrade.bin - $(call Image/Build/$(1),$(1)) - $(call BuildFirmware/Legacy,$(1),laguna_spi,$(LEGACY_SPI_KERNEL_SIZE)) - $(call BuildFirmware/Legacy,$(1),laguna_nor,$(LEGACY_NOR_KERNEL_SIZE)) -endef - -define Image/Build/squashfs - $(call prepare_generic_squashfs,$(BIN_DIR)/$(IMG_PREFIX)-$(1)-sysupgrade.bin) -endef - -$(eval $(call BuildImage)) diff --git a/target/linux/cns3xxx/patches-4.19/000-cns3xxx_arch_include.patch b/target/linux/cns3xxx/patches-4.19/000-cns3xxx_arch_include.patch deleted file mode 100644 index 3ae759021a..0000000000 --- a/target/linux/cns3xxx/patches-4.19/000-cns3xxx_arch_include.patch +++ /dev/null @@ -1,9 +0,0 @@ ---- a/arch/arm/mach-cns3xxx/Makefile -+++ b/arch/arm/mach-cns3xxx/Makefile -@@ -1,4 +1,6 @@ - # SPDX-License-Identifier: GPL-2.0 -+ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/$(src)/include -+ - obj-$(CONFIG_ARCH_CNS3XXX) += cns3xxx.o - cns3xxx-y += core.o pm.o - cns3xxx-$(CONFIG_ATAGS) += devices.o diff --git a/target/linux/cns3xxx/patches-4.19/001-arm_openwrt_machtypes.patch b/target/linux/cns3xxx/patches-4.19/001-arm_openwrt_machtypes.patch deleted file mode 100644 index f323deee47..0000000000 --- a/target/linux/cns3xxx/patches-4.19/001-arm_openwrt_machtypes.patch +++ /dev/null @@ -1,10 +0,0 @@ ---- a/arch/arm/tools/mach-types -+++ b/arch/arm/tools/mach-types -@@ -449,6 +449,7 @@ gplugd MACH_GPLUGD GPLUGD 2625 - qsd8x50a_st1_5 MACH_QSD8X50A_ST1_5 QSD8X50A_ST1_5 2627 - mx23evk MACH_MX23EVK MX23EVK 2629 - ap4evb MACH_AP4EVB AP4EVB 2630 -+gw2388 MACH_GW2388 GW2388 2635 - mityomapl138 MACH_MITYOMAPL138 MITYOMAPL138 2650 - guruplug MACH_GURUPLUG GURUPLUG 2659 - spear310 MACH_SPEAR310 SPEAR310 2660 diff --git a/target/linux/cns3xxx/patches-4.19/010-arm_introduce-dma-fiq-irq-broadcast.patch b/target/linux/cns3xxx/patches-4.19/010-arm_introduce-dma-fiq-irq-broadcast.patch deleted file mode 100644 index 45f8feead4..0000000000 --- a/target/linux/cns3xxx/patches-4.19/010-arm_introduce-dma-fiq-irq-broadcast.patch +++ /dev/null @@ -1,80 +0,0 @@ ---- a/arch/arm/include/asm/glue-cache.h -+++ b/arch/arm/include/asm/glue-cache.h -@@ -156,9 +156,15 @@ static inline void nop_dma_unmap_area(co - #define __cpuc_flush_user_range __glue(_CACHE,_flush_user_cache_range) - #define __cpuc_coherent_kern_range __glue(_CACHE,_coherent_kern_range) - #define __cpuc_coherent_user_range __glue(_CACHE,_coherent_user_range) --#define __cpuc_flush_dcache_area __glue(_CACHE,_flush_kern_dcache_area) - --#define dmac_flush_range __glue(_CACHE,_dma_flush_range) -+#ifndef CONFIG_DMA_CACHE_FIQ_BROADCAST -+# define __cpuc_flush_dcache_area __glue(_CACHE,_flush_kern_dcache_area) -+# define dmac_flush_range __glue(_CACHE,_dma_flush_range) -+#else -+# define __cpuc_flush_dcache_area __glue(fiq,_flush_kern_dcache_area) -+# define dmac_flush_range __glue(fiq,_dma_flush_range) -+#endif -+ - #endif - - #endif ---- a/arch/arm/mm/Kconfig -+++ b/arch/arm/mm/Kconfig -@@ -916,6 +916,17 @@ config DMA_CACHE_RWFO - in hardware, other workarounds are needed (e.g. cache - maintenance broadcasting in software via FIQ). - -+config DMA_CACHE_FIQ_BROADCAST -+ bool "Enable fiq broadcast DMA cache maintenance" -+ depends on CPU_V6K && SMP -+ select FIQ -+ help -+ The Snoop Control Unit on ARM11MPCore does not detect the -+ cache maintenance operations and the dma_{map,unmap}_area() -+ functions may leave stale cache entries on other CPUs. By -+ enabling this option, fiq broadcast in the ARMv6 -+ DMA cache maintenance functions is performed. -+ - config OUTER_CACHE - bool - ---- a/arch/arm/mm/flush.c -+++ b/arch/arm/mm/flush.c -@@ -319,6 +319,7 @@ void __sync_icache_dcache(pte_t pteval) - void flush_dcache_page(struct page *page) - { - struct address_space *mapping; -+ bool skip_broadcast = true; - - /* - * The zero page is never written to, so never has any dirty -@@ -335,7 +336,10 @@ void flush_dcache_page(struct page *page - - mapping = page_mapping_file(page); - -- if (!cache_ops_need_broadcast() && -+#ifndef CONFIG_DMA_CACHE_FIQ_BROADCAST -+ skip_broadcast = !cache_ops_need_broadcast(); -+#endif -+ if (skip_broadcast && - mapping && !page_mapcount(page)) - clear_bit(PG_dcache_clean, &page->flags); - else { ---- a/arch/arm/mm/dma.h -+++ b/arch/arm/mm/dma.h -@@ -5,8 +5,13 @@ - #include - - #ifndef MULTI_CACHE --#define dmac_map_area __glue(_CACHE,_dma_map_area) --#define dmac_unmap_area __glue(_CACHE,_dma_unmap_area) -+#ifndef CONFIG_DMA_CACHE_FIQ_BROADCAST -+# define dmac_map_area __glue(_CACHE,_dma_map_area) -+# define dmac_unmap_area __glue(_CACHE,_dma_unmap_area) -+#else -+# define dmac_map_area __glue(fiq,_dma_map_area) -+# define dmac_unmap_area __glue(fiq,_dma_unmap_area) -+#endif - - /* - * These are private to the dma-mapping API. Do not use directly. diff --git a/target/linux/cns3xxx/patches-4.19/020-watchdog_support.patch b/target/linux/cns3xxx/patches-4.19/020-watchdog_support.patch deleted file mode 100644 index e626ff29b7..0000000000 --- a/target/linux/cns3xxx/patches-4.19/020-watchdog_support.patch +++ /dev/null @@ -1,184 +0,0 @@ -Add a watchdog driver for ARM MPcore processors. - -Signed-off-by: Felix Fietkau ---- a/drivers/watchdog/Kconfig -+++ b/drivers/watchdog/Kconfig -@@ -375,6 +375,13 @@ config KS8695_WATCHDOG - Watchdog timer embedded into KS8695 processor. This will reboot your - system when the timeout is reached. - -+config MPCORE_WATCHDOG -+ tristate "MPcore watchdog" -+ depends on HAVE_ARM_TWD -+ select WATCHDOG_CORE -+ help -+ Watchdog timer embedded into the MPcore system -+ - config HAVE_S3C2410_WATCHDOG - bool - help ---- a/drivers/watchdog/Makefile -+++ b/drivers/watchdog/Makefile -@@ -49,6 +49,7 @@ obj-$(CONFIG_977_WATCHDOG) += wdt977.o - obj-$(CONFIG_FTWDT010_WATCHDOG) += ftwdt010_wdt.o - obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o - obj-$(CONFIG_KS8695_WATCHDOG) += ks8695_wdt.o -+obj-$(CONFIG_MPCORE_WATCHDOG) += mpcore_wdt.o - obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o - obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o - obj-$(CONFIG_SAMA5D4_WATCHDOG) += sama5d4_wdt.o ---- /dev/null -+++ b/drivers/watchdog/mpcore_wdt.c -@@ -0,0 +1,118 @@ -+/* -+ * Watchdog driver for ARM MPcore -+ * -+ * Copyright (C) 2017 Felix Fietkau -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+static void __iomem *wdt_base; -+static int wdt_timeout = 60; -+ -+static int mpcore_wdt_keepalive(struct watchdog_device *wdd) -+{ -+ static int perturb; -+ u32 count; -+ -+ count = (twd_timer_get_rate() / 256) * wdt_timeout; -+ -+ /* Reload register needs a different value on each refresh */ -+ count += perturb; -+ perturb = !perturb; -+ -+ iowrite32(count, wdt_base + TWD_WDOG_LOAD); -+ -+ return 0; -+} -+ -+static int mpcore_wdt_start(struct watchdog_device *wdd) -+{ -+ mpcore_wdt_keepalive(wdd); -+ -+ /* prescale = 256, mode = 1, enable = 1 */ -+ iowrite32(0x0000FF09, wdt_base + TWD_WDOG_CONTROL); -+ -+ return 0; -+} -+ -+static int mpcore_wdt_stop(struct watchdog_device *wdd) -+{ -+ iowrite32(0x12345678, wdt_base + TWD_WDOG_DISABLE); -+ iowrite32(0x87654321, wdt_base + TWD_WDOG_DISABLE); -+ iowrite32(0x0, wdt_base + TWD_WDOG_CONTROL); -+ -+ return 0; -+} -+ -+static int mpcore_wdt_set_timeout(struct watchdog_device *wdd, -+ unsigned int timeout) -+{ -+ mpcore_wdt_stop(wdd); -+ wdt_timeout = timeout; -+ mpcore_wdt_start(wdd); -+ -+ return 0; -+} -+ -+static const struct watchdog_info mpcore_wdt_info = { -+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, -+ .identity = "MPcore Watchdog", -+}; -+ -+static const struct watchdog_ops mpcore_wdt_ops = { -+ .owner = THIS_MODULE, -+ .start = mpcore_wdt_start, -+ .stop = mpcore_wdt_stop, -+ .ping = mpcore_wdt_keepalive, -+ .set_timeout = mpcore_wdt_set_timeout, -+}; -+ -+static struct watchdog_device mpcore_wdt = { -+ .info = &mpcore_wdt_info, -+ .ops = &mpcore_wdt_ops, -+ .min_timeout = 1, -+ .max_timeout = 65535, -+}; -+ -+static int mpcore_wdt_probe(struct platform_device *pdev) -+{ -+ struct resource *res; -+ unsigned long rate = twd_timer_get_rate(); -+ -+ pr_info("MPCore WD init. clockrate: %lu prescaler: %u countrate: %lu timeout: %us\n", rate, 256, rate / 256, wdt_timeout); -+ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (!res) -+ return -ENODEV; -+ -+ wdt_base = devm_ioremap_resource(&pdev->dev, res); -+ if (IS_ERR(wdt_base)) -+ return PTR_ERR(wdt_base); -+ -+ watchdog_register_device(&mpcore_wdt); -+ return 0; -+} -+ -+static int mpcore_wdt_remove(struct platform_device *dev) -+{ -+ watchdog_unregister_device(&mpcore_wdt); -+ return 0; -+} -+ -+static struct platform_driver mpcore_wdt_driver = { -+ .probe = mpcore_wdt_probe, -+ .remove = mpcore_wdt_remove, -+ .driver = { -+ .name = "mpcore_wdt", -+ }, -+}; -+ -+module_platform_driver(mpcore_wdt_driver); -+MODULE_AUTHOR("Felix Fietkau "); -+MODULE_LICENSE("GPL"); ---- a/arch/arm/include/asm/smp_twd.h -+++ b/arch/arm/include/asm/smp_twd.h -@@ -34,5 +34,6 @@ struct twd_local_timer name __initdata = - }; - - int twd_local_timer_register(struct twd_local_timer *); -+unsigned long twd_timer_get_rate(void); - - #endif ---- a/arch/arm/kernel/smp_twd.c -+++ b/arch/arm/kernel/smp_twd.c -@@ -15,6 +15,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -380,6 +381,14 @@ int __init twd_local_timer_register(stru - return twd_local_timer_common_register(NULL); - } - -+/* Needed by mpcore_wdt */ -+unsigned long twd_timer_get_rate(void) -+{ -+ return twd_timer_rate; -+} -+EXPORT_SYMBOL_GPL(twd_timer_get_rate); -+ -+ - #ifdef CONFIG_OF - static int __init twd_local_timer_of_register(struct device_node *np) - { diff --git a/target/linux/cns3xxx/patches-4.19/025-smp_support.patch b/target/linux/cns3xxx/patches-4.19/025-smp_support.patch deleted file mode 100644 index dc8e4b03c2..0000000000 --- a/target/linux/cns3xxx/patches-4.19/025-smp_support.patch +++ /dev/null @@ -1,30 +0,0 @@ ---- a/arch/arm/mach-cns3xxx/Makefile -+++ b/arch/arm/mach-cns3xxx/Makefile -@@ -6,3 +6,5 @@ cns3xxx-y += core.o pm.o - cns3xxx-$(CONFIG_ATAGS) += devices.o - cns3xxx-$(CONFIG_PCI) += pcie.o - cns3xxx-$(CONFIG_MACH_CNS3420VB) += cns3420vb.o -+cns3xxx-$(CONFIG_SMP) += platsmp.o headsmp.o -+cns3xxx-$(CONFIG_HOTPLUG_CPU) += hotplug.o ---- a/arch/arm/mach-cns3xxx/Kconfig -+++ b/arch/arm/mach-cns3xxx/Kconfig -@@ -3,6 +3,9 @@ menuconfig ARCH_CNS3XXX - bool "Cavium Networks CNS3XXX family" - depends on ARCH_MULTI_V6 - select ARM_GIC -+ select HAVE_ARM_SCU if SMP -+ select HAVE_ARM_TWD -+ select HAVE_SMP - help - Support for Cavium Networks CNS3XXX platform. - ---- a/arch/arm/mach-cns3xxx/core.h -+++ b/arch/arm/mach-cns3xxx/core.h -@@ -13,6 +13,7 @@ - - #include - -+extern struct smp_operations cns3xxx_smp_ops; - extern void cns3xxx_timer_init(void); - - #ifdef CONFIG_CACHE_L2X0 diff --git a/target/linux/cns3xxx/patches-4.19/030-pcie_clock.patch b/target/linux/cns3xxx/patches-4.19/030-pcie_clock.patch deleted file mode 100644 index 66c3a99ae1..0000000000 --- a/target/linux/cns3xxx/patches-4.19/030-pcie_clock.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/arch/arm/mach-cns3xxx/pcie.c -+++ b/arch/arm/mach-cns3xxx/pcie.c -@@ -281,8 +281,6 @@ void __init cns3xxx_pcie_init_late(void) - "imprecise external abort"); - - for (i = 0; i < ARRAY_SIZE(cns3xxx_pcie); i++) { -- cns3xxx_pwr_clk_en(0x1 << PM_CLK_GATE_REG_OFFSET_PCIE(i)); -- cns3xxx_pwr_soft_rst(0x1 << PM_SOFT_RST_REG_OFFST_PCIE(i)); - cns3xxx_pcie_check_link(&cns3xxx_pcie[i]); - cns3xxx_pcie_hw_init(&cns3xxx_pcie[i]); - private_data = &cns3xxx_pcie[i]; diff --git a/target/linux/cns3xxx/patches-4.19/040-fiq_support.patch b/target/linux/cns3xxx/patches-4.19/040-fiq_support.patch deleted file mode 100644 index bee8c1ef24..0000000000 --- a/target/linux/cns3xxx/patches-4.19/040-fiq_support.patch +++ /dev/null @@ -1,40 +0,0 @@ ---- a/arch/arm/mach-cns3xxx/Kconfig -+++ b/arch/arm/mach-cns3xxx/Kconfig -@@ -6,6 +6,7 @@ menuconfig ARCH_CNS3XXX - select HAVE_ARM_SCU if SMP - select HAVE_ARM_TWD - select HAVE_SMP -+ select FIQ - help - Support for Cavium Networks CNS3XXX platform. - ---- a/arch/arm/mach-cns3xxx/Makefile -+++ b/arch/arm/mach-cns3xxx/Makefile -@@ -6,5 +6,5 @@ cns3xxx-y += core.o pm.o - cns3xxx-$(CONFIG_ATAGS) += devices.o - cns3xxx-$(CONFIG_PCI) += pcie.o - cns3xxx-$(CONFIG_MACH_CNS3420VB) += cns3420vb.o --cns3xxx-$(CONFIG_SMP) += platsmp.o headsmp.o -+cns3xxx-$(CONFIG_SMP) += platsmp.o headsmp.o cns3xxx_fiq.o - cns3xxx-$(CONFIG_HOTPLUG_CPU) += hotplug.o ---- a/arch/arm/mach-cns3xxx/cns3xxx.h -+++ b/arch/arm/mach-cns3xxx/cns3xxx.h -@@ -261,6 +261,7 @@ - #define MISC_PCIE_INT_MASK(x) MISC_MEM_MAP(0x978 + (x) * 0x100) - #define MISC_PCIE_INT_STATUS(x) MISC_MEM_MAP(0x97C + (x) * 0x100) - -+#define MISC_FIQ_CPU(x) MISC_MEM_MAP(0xA58 - (x) * 0x4) - /* - * Power management and clock control - */ ---- a/arch/arm/mm/Kconfig -+++ b/arch/arm/mm/Kconfig -@@ -899,7 +899,7 @@ config VDSO - - config DMA_CACHE_RWFO - bool "Enable read/write for ownership DMA cache maintenance" -- depends on CPU_V6K && SMP -+ depends on CPU_V6K && SMP && !ARCH_CNS3XXX - default y - help - The Snoop Control Unit on ARM11MPCore does not detect the diff --git a/target/linux/cns3xxx/patches-4.19/045-twd_base.patch b/target/linux/cns3xxx/patches-4.19/045-twd_base.patch deleted file mode 100644 index e722b01bbc..0000000000 --- a/target/linux/cns3xxx/patches-4.19/045-twd_base.patch +++ /dev/null @@ -1,43 +0,0 @@ ---- a/arch/arm/mach-cns3xxx/core.c -+++ b/arch/arm/mach-cns3xxx/core.c -@@ -17,6 +17,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -26,6 +27,8 @@ - #include "core.h" - #include "pm.h" - -+#define IRQ_LOCALTIMER 29 -+ - static struct map_desc cns3xxx_io_desc[] __initdata = { - { - .virtual = CNS3XXX_TC11MP_SCU_BASE_VIRT, -@@ -198,6 +201,15 @@ static struct irqaction cns3xxx_timer_ir - .handler = cns3xxx_timer_interrupt, - }; - -+static void __init cns3xxx_init_twd(void) -+{ -+ static DEFINE_TWD_LOCAL_TIMER(cns3xx_twd_local_timer, -+ CNS3XXX_TC11MP_TWD_BASE, -+ IRQ_LOCALTIMER); -+ -+ twd_local_timer_register(&cns3xx_twd_local_timer); -+} -+ - /* - * Set up the clock source and clock events devices - */ -@@ -251,6 +263,7 @@ static void __init __cns3xxx_timer_init( - setup_irq(timer_irq, &cns3xxx_timer_irq); - - cns3xxx_clockevents_init(timer_irq); -+ cns3xxx_init_twd(); - } - - void __init cns3xxx_timer_init(void) diff --git a/target/linux/cns3xxx/patches-4.19/055-pcie_io.patch b/target/linux/cns3xxx/patches-4.19/055-pcie_io.patch deleted file mode 100644 index 4680853e6e..0000000000 --- a/target/linux/cns3xxx/patches-4.19/055-pcie_io.patch +++ /dev/null @@ -1,19 +0,0 @@ ---- a/arch/arm/mach-cns3xxx/core.c -+++ b/arch/arm/mach-cns3xxx/core.c -@@ -81,6 +81,16 @@ static struct map_desc cns3xxx_io_desc[] - .pfn = __phys_to_pfn(CNS3XXX_PCIE1_CFG1_BASE), - .length = SZ_16M, - .type = MT_DEVICE, -+ }, { -+ .virtual = CNS3XXX_PCIE0_IO_BASE_VIRT, -+ .pfn = __phys_to_pfn(CNS3XXX_PCIE0_IO_BASE), -+ .length = SZ_16M, -+ .type = MT_DEVICE, -+ }, { -+ .virtual = CNS3XXX_PCIE1_IO_BASE_VIRT, -+ .pfn = __phys_to_pfn(CNS3XXX_PCIE1_IO_BASE), -+ .length = SZ_16M, -+ .type = MT_DEVICE, - #endif - }, - }; diff --git a/target/linux/cns3xxx/patches-4.19/060-pcie_abort.patch b/target/linux/cns3xxx/patches-4.19/060-pcie_abort.patch deleted file mode 100644 index 87b1526c3a..0000000000 --- a/target/linux/cns3xxx/patches-4.19/060-pcie_abort.patch +++ /dev/null @@ -1,109 +0,0 @@ ---- a/arch/arm/mach-cns3xxx/pcie.c -+++ b/arch/arm/mach-cns3xxx/pcie.c -@@ -86,6 +86,79 @@ static void __iomem *cns3xxx_pci_map_bus - return base + where + (devfn << 12); - } - -+static inline int check_master_abort(struct pci_bus *bus, unsigned int devfn, int where) -+{ -+ struct cns3xxx_pcie *cnspci = pbus_to_cnspci(bus); -+ -+ /* check PCI-compatible status register after access */ -+ if (cnspci->linked) { -+ void __iomem *host_base; -+ u32 sreg, ereg; -+ -+ host_base = (void __iomem *) cnspci->cfg_bases[CNS3XXX_HOST_TYPE].virtual; -+ sreg = __raw_readw(host_base + 0x6) & 0xF900; -+ ereg = __raw_readl(host_base + 0x104); // Uncorrectable Error Status Reg -+ -+ if (sreg | ereg) { -+ /* SREG: -+ * BIT15 - Detected Parity Error -+ * BIT14 - Signaled System Error -+ * BIT13 - Received Master Abort -+ * BIT12 - Received Target Abort -+ * BIT11 - Signaled Target Abort -+ * BIT08 - Master Data Parity Error -+ * -+ * EREG: -+ * BIT20 - Unsupported Request -+ * BIT19 - ECRC -+ * BIT18 - Malformed TLP -+ * BIT17 - Receiver Overflow -+ * BIT16 - Unexpected Completion -+ * BIT15 - Completer Abort -+ * BIT14 - Completion Timeout -+ * BIT13 - Flow Control Protocol Error -+ * BIT12 - Poisoned TLP -+ * BIT04 - Data Link Protocol Error -+ * -+ * TODO: see Documentation/pci-error-recovery.txt -+ * implement error_detected handler -+ */ -+/* -+ printk("pci error: %04d:%02x:%02x.%02x sreg=0x%04x ereg=0x%08x", pci_domain_nr(bus), bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), sreg, ereg); -+ if (sreg & BIT(15)) printk(" SERR"); -+ if (sreg & BIT(13)) printk(" TABRT"); -+ if (sreg & BIT( 8)) printk(" MPERR"); -+ -+ if (ereg & BIT(20)) printk(" Unsup"); -+ if (ereg & BIT(19)) printk(" ECRC"); -+ if (ereg & BIT(18)) printk(" MTLP"); -+ if (ereg & BIT(17)) printk(" OFLOW"); -+ if (ereg & BIT(16)) printk(" Unex"); -+ if (ereg & BIT(15)) printk(" ABRT"); -+ if (ereg & BIT(14)) printk(" COMPTO"); -+ if (ereg & BIT(13)) printk(" FLOW"); -+ if (ereg & BIT(12)) printk(" PTLP"); -+ if (ereg & BIT( 4)) printk(" DLINK"); -+ printk("\n"); -+*/ -+ pr_debug("%s failed port%d sreg=0x%04x\n", __func__, -+ pci_domain_nr(bus), sreg); -+ -+ /* make sure the status bits are reset */ -+ __raw_writew(sreg, host_base + 6); -+ __raw_writel(ereg, host_base + 0x104); -+ return 1; -+ } -+ } -+ else -+ return 1; -+ -+ return 0; -+} -+ - static int cns3xxx_pci_read_config(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 *val) - { -@@ -95,6 +168,11 @@ static int cns3xxx_pci_read_config(struc - - ret = pci_generic_config_read(bus, devfn, where, size, val); - -+ if (check_master_abort(bus, devfn, where)) { -+ printk(KERN_ERR "pci error: %04d:%02x:%02x.%02x %02x(%d)= master_abort on read\n", pci_domain_nr(bus), bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), where, size); -+ return PCIBIOS_DEVICE_NOT_FOUND; -+ } -+ - if (ret == PCIBIOS_SUCCESSFUL && !bus->number && !devfn && - (where & 0xffc) == PCI_CLASS_REVISION) - /* -@@ -257,8 +335,14 @@ static void __init cns3xxx_pcie_hw_init( - static int cns3xxx_pcie_abort_handler(unsigned long addr, unsigned int fsr, - struct pt_regs *regs) - { -+#if 0 -+/* R14_ABORT = PC+4 for XSCALE but not ARM11MPCORE -+ * ignore imprecise aborts and use PCI-compatible Status register to -+ * determine errors instead -+ */ - if (fsr & (1 << 10)) - regs->ARM_pc += 4; -+#endif - return 0; - } - diff --git a/target/linux/cns3xxx/patches-4.19/065-pcie_skip_inactive.patch b/target/linux/cns3xxx/patches-4.19/065-pcie_skip_inactive.patch deleted file mode 100644 index b8b5f273a1..0000000000 --- a/target/linux/cns3xxx/patches-4.19/065-pcie_skip_inactive.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/arch/arm/mach-cns3xxx/pcie.c -+++ b/arch/arm/mach-cns3xxx/pcie.c -@@ -366,6 +366,8 @@ void __init cns3xxx_pcie_init_late(void) - - for (i = 0; i < ARRAY_SIZE(cns3xxx_pcie); i++) { - cns3xxx_pcie_check_link(&cns3xxx_pcie[i]); -+ if (!cns3xxx_pcie[i].linked) -+ continue; - cns3xxx_pcie_hw_init(&cns3xxx_pcie[i]); - private_data = &cns3xxx_pcie[i]; - pci_common_init(&hw_pci); diff --git a/target/linux/cns3xxx/patches-4.19/070-i2c_support.patch b/target/linux/cns3xxx/patches-4.19/070-i2c_support.patch deleted file mode 100644 index 95768f64a8..0000000000 --- a/target/linux/cns3xxx/patches-4.19/070-i2c_support.patch +++ /dev/null @@ -1,31 +0,0 @@ ---- a/drivers/i2c/busses/Kconfig -+++ b/drivers/i2c/busses/Kconfig -@@ -460,6 +460,18 @@ config I2C_CBUS_GPIO - This driver can also be built as a module. If so, the module - will be called i2c-cbus-gpio. - -+config I2C_CNS3XXX -+ tristate "Cavium CNS3xxx I2C driver" -+ depends on ARCH_CNS3XXX -+ help -+ Support for Cavium CNS3xxx I2C controller driver. -+ -+ This driver can also be built as a module. If so, the module -+ will be called i2c-cns3xxx. -+ -+ Please note that this driver might be needed to bring up other -+ devices such as Cavium CNS3xxx Ethernet. -+ - config I2C_CPM - tristate "Freescale CPM1 or CPM2 (MPC8xx/826x)" - depends on CPM1 || CPM2 ---- a/drivers/i2c/busses/Makefile -+++ b/drivers/i2c/busses/Makefile -@@ -131,6 +131,7 @@ obj-$(CONFIG_I2C_VIPERBOARD) += i2c-vipe - obj-$(CONFIG_I2C_ACORN) += i2c-acorn.o - obj-$(CONFIG_I2C_BCM_KONA) += i2c-bcm-kona.o - obj-$(CONFIG_I2C_BRCMSTB) += i2c-brcmstb.o -+obj-$(CONFIG_I2C_CNS3XXX) += i2c-cns3xxx.o - obj-$(CONFIG_I2C_CROS_EC_TUNNEL) += i2c-cros-ec-tunnel.o - obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o - obj-$(CONFIG_I2C_MLXCPLD) += i2c-mlxcpld.o diff --git a/target/linux/cns3xxx/patches-4.19/075-spi_support.patch b/target/linux/cns3xxx/patches-4.19/075-spi_support.patch deleted file mode 100644 index afa6d862df..0000000000 --- a/target/linux/cns3xxx/patches-4.19/075-spi_support.patch +++ /dev/null @@ -1,51 +0,0 @@ ---- a/drivers/spi/Kconfig -+++ b/drivers/spi/Kconfig -@@ -181,6 +181,13 @@ config SPI_CLPS711X - This enables dedicated general purpose SPI/Microwire1-compatible - master mode interface (SSI1) for CLPS711X-based CPUs. - -+config SPI_CNS3XXX -+ tristate "CNS3XXX SPI controller" -+ depends on ARCH_CNS3XXX && SPI_MASTER -+ select SPI_BITBANG -+ help -+ This enables using the CNS3XXX SPI controller in master mode. -+ - config SPI_COLDFIRE_QSPI - tristate "Freescale Coldfire QSPI controller" - depends on (M520x || M523x || M5249 || M525x || M527x || M528x || M532x) ---- a/drivers/spi/Makefile -+++ b/drivers/spi/Makefile -@@ -28,6 +28,7 @@ obj-$(CONFIG_SPI_BITBANG) += spi-bitban - obj-$(CONFIG_SPI_BUTTERFLY) += spi-butterfly.o - obj-$(CONFIG_SPI_CADENCE) += spi-cadence.o - obj-$(CONFIG_SPI_CLPS711X) += spi-clps711x.o -+obj-$(CONFIG_SPI_CNS3XXX) += spi-cns3xxx.o - obj-$(CONFIG_SPI_COLDFIRE_QSPI) += spi-coldfire-qspi.o - obj-$(CONFIG_SPI_DAVINCI) += spi-davinci.o - obj-$(CONFIG_SPI_DLN2) += spi-dln2.o ---- a/include/linux/spi/spi.h -+++ b/include/linux/spi/spi.h -@@ -795,6 +795,10 @@ struct spi_transfer { - u32 speed_hz; - - struct list_head transfer_list; -+ -+#ifdef CONFIG_ARCH_CNS3XXX -+ unsigned last_in_message_list; -+#endif - }; - - /** ---- a/drivers/spi/spi.c -+++ b/drivers/spi/spi.c -@@ -1020,6 +1020,9 @@ static int spi_transfer_one_message(stru - list_for_each_entry(xfer, &msg->transfers, transfer_list) { - trace_spi_transfer_start(msg, xfer); - -+ xfer->last_in_message_list = -+ list_is_last(&xfer->transfer_list, &msg->transfers); -+ - spi_statistics_add_transfer_stats(statm, xfer, ctlr); - spi_statistics_add_transfer_stats(stats, xfer, ctlr); - diff --git a/target/linux/cns3xxx/patches-4.19/080-sata_support.patch b/target/linux/cns3xxx/patches-4.19/080-sata_support.patch deleted file mode 100644 index 65a5275bec..0000000000 --- a/target/linux/cns3xxx/patches-4.19/080-sata_support.patch +++ /dev/null @@ -1,40 +0,0 @@ ---- a/drivers/ata/ahci_platform.c -+++ b/drivers/ata/ahci_platform.c -@@ -44,11 +44,18 @@ static struct scsi_host_template ahci_pl - AHCI_SHT(DRV_NAME), - }; - -+static const struct ata_port_info cns3xxx_port_info = { -+ .flags = AHCI_FLAG_COMMON, -+ .pio_mask = ATA_PIO4, -+ .udma_mask = ATA_UDMA6, -+ .port_ops = &ahci_pmp_retry_srst_ops, -+}; -+ - static int ahci_probe(struct platform_device *pdev) - { - struct device *dev = &pdev->dev; - struct ahci_host_priv *hpriv; -- const struct ata_port_info *port; -+ const struct ata_port_info *port = &ahci_port_info; - int rc; - - hpriv = ahci_platform_get_resources(pdev, -@@ -66,9 +73,14 @@ static int ahci_probe(struct platform_de - if (of_device_is_compatible(dev->of_node, "hisilicon,hisi-ahci")) - hpriv->flags |= AHCI_HFLAG_NO_FBS | AHCI_HFLAG_NO_NCQ; - -- port = acpi_device_get_match_data(dev); -- if (!port) -- port = &ahci_port_info; -+ if (IS_ENABLED(CONFIG_ARCH_CNS3XXX)) { -+ port = &cns3xxx_port_info; -+ } -+ else { -+ port = acpi_device_get_match_data(dev); -+ if (!port) -+ port = &ahci_port_info; -+ } - - rc = ahci_platform_init_host(pdev, hpriv, port, - &ahci_platform_sht); diff --git a/target/linux/cns3xxx/patches-4.19/090-timers.patch b/target/linux/cns3xxx/patches-4.19/090-timers.patch deleted file mode 100644 index b3810f8c9b..0000000000 --- a/target/linux/cns3xxx/patches-4.19/090-timers.patch +++ /dev/null @@ -1,105 +0,0 @@ ---- a/arch/arm/mach-cns3xxx/core.c -+++ b/arch/arm/mach-cns3xxx/core.c -@@ -138,6 +138,7 @@ static int cns3xxx_set_oneshot(struct cl - - /* period set, and timer enabled in 'next_event' hook */ - ctrl |= (1 << 2) | (1 << 9); -+ writel(0, cns3xxx_tmr1 + TIMER1_AUTO_RELOAD_OFFSET); - writel(ctrl, cns3xxx_tmr1 + TIMER1_2_CONTROL_OFFSET); - return 0; - } -@@ -148,7 +149,7 @@ static int cns3xxx_set_periodic(struct c - int pclk = cns3xxx_cpu_clock() / 8; - int reload; - -- reload = pclk * 20 / (3 * HZ) * 0x25000; -+ reload = pclk * 1000000 / HZ; - writel(reload, cns3xxx_tmr1 + TIMER1_AUTO_RELOAD_OFFSET); - ctrl |= (1 << 0) | (1 << 2) | (1 << 9); - writel(ctrl, cns3xxx_tmr1 + TIMER1_2_CONTROL_OFFSET); -@@ -175,8 +176,8 @@ static struct clock_event_device cns3xxx - .set_state_oneshot = cns3xxx_set_oneshot, - .tick_resume = cns3xxx_shutdown, - .set_next_event = cns3xxx_timer_set_next_event, -- .rating = 350, -- .cpumask = cpu_all_mask, -+ .rating = 300, -+ .cpumask = cpu_possible_mask, - }; - - static void __init cns3xxx_clockevents_init(unsigned int timer_irq) -@@ -220,6 +221,32 @@ static void __init cns3xxx_init_twd(void - twd_local_timer_register(&cns3xx_twd_local_timer); - } - -+static u64 cns3xxx_get_cycles(struct clocksource *cs) -+{ -+ u64 val; -+ -+ val = readl(cns3xxx_tmr1 + TIMER_FREERUN_CONTROL_OFFSET); -+ val &= 0xffff; -+ -+ return ((val << 32) | readl(cns3xxx_tmr1 + TIMER_FREERUN_OFFSET)); -+} -+ -+static struct clocksource clocksource_cns3xxx = { -+ .name = "freerun", -+ .rating = 200, -+ .read = cns3xxx_get_cycles, -+ .mask = CLOCKSOURCE_MASK(48), -+ .flags = CLOCK_SOURCE_IS_CONTINUOUS, -+}; -+ -+static void __init cns3xxx_clocksource_init(void) -+{ -+ /* Reset the FreeRunning counter */ -+ writel((1 << 16), cns3xxx_tmr1 + TIMER_FREERUN_CONTROL_OFFSET); -+ -+ clocksource_register_khz(&clocksource_cns3xxx, 100); -+} -+ - /* - * Set up the clock source and clock events devices - */ -@@ -237,13 +264,12 @@ static void __init __cns3xxx_timer_init( - /* stop free running timer3 */ - writel(0, cns3xxx_tmr1 + TIMER_FREERUN_CONTROL_OFFSET); - -- /* timer1 */ -- writel(0x5C800, cns3xxx_tmr1 + TIMER1_COUNTER_OFFSET); -- writel(0x5C800, cns3xxx_tmr1 + TIMER1_AUTO_RELOAD_OFFSET); -- - writel(0, cns3xxx_tmr1 + TIMER1_MATCH_V1_OFFSET); - writel(0, cns3xxx_tmr1 + TIMER1_MATCH_V2_OFFSET); - -+ val = (cns3xxx_cpu_clock() >> 3) * 1000000 / HZ; -+ writel(val, cns3xxx_tmr1 + TIMER1_COUNTER_OFFSET); -+ - /* mask irq, non-mask timer1 overflow */ - irq_mask = readl(cns3xxx_tmr1 + TIMER1_2_INTERRUPT_MASK_OFFSET); - irq_mask &= ~(1 << 2); -@@ -255,23 +281,9 @@ static void __init __cns3xxx_timer_init( - val |= (1 << 9); - writel(val, cns3xxx_tmr1 + TIMER1_2_CONTROL_OFFSET); - -- /* timer2 */ -- writel(0, cns3xxx_tmr1 + TIMER2_MATCH_V1_OFFSET); -- writel(0, cns3xxx_tmr1 + TIMER2_MATCH_V2_OFFSET); -- -- /* mask irq */ -- irq_mask = readl(cns3xxx_tmr1 + TIMER1_2_INTERRUPT_MASK_OFFSET); -- irq_mask |= ((1 << 3) | (1 << 4) | (1 << 5)); -- writel(irq_mask, cns3xxx_tmr1 + TIMER1_2_INTERRUPT_MASK_OFFSET); -- -- /* down counter */ -- val = readl(cns3xxx_tmr1 + TIMER1_2_CONTROL_OFFSET); -- val |= (1 << 10); -- writel(val, cns3xxx_tmr1 + TIMER1_2_CONTROL_OFFSET); -- -- /* Make irqs happen for the system timer */ - setup_irq(timer_irq, &cns3xxx_timer_irq); - -+ cns3xxx_clocksource_init(); - cns3xxx_clockevents_init(timer_irq); - cns3xxx_init_twd(); - } diff --git a/target/linux/cns3xxx/patches-4.19/093-add-virt-pci-io-mapping.patch b/target/linux/cns3xxx/patches-4.19/093-add-virt-pci-io-mapping.patch deleted file mode 100644 index 0fa7ed483f..0000000000 --- a/target/linux/cns3xxx/patches-4.19/093-add-virt-pci-io-mapping.patch +++ /dev/null @@ -1,41 +0,0 @@ ---- a/arch/arm/mach-cns3xxx/cns3xxx.h -+++ b/arch/arm/mach-cns3xxx/cns3xxx.h -@@ -162,11 +162,13 @@ - #define CNS3XXX_L2C_BASE 0x92000000 /* L2 Cache Control */ - - #define CNS3XXX_PCIE0_MEM_BASE 0xA0000000 /* PCIe Port 0 IO/Memory Space */ -+#define CNS3XXX_PCIE0_MEM_BASE_VIRT 0xE0000000 - - #define CNS3XXX_PCIE0_HOST_BASE 0xAB000000 /* PCIe Port 0 RC Base */ - #define CNS3XXX_PCIE0_HOST_BASE_VIRT 0xE1000000 - - #define CNS3XXX_PCIE0_IO_BASE 0xAC000000 /* PCIe Port 0 */ -+#define CNS3XXX_PCIE0_IO_BASE_VIRT 0xE2000000 - - #define CNS3XXX_PCIE0_CFG0_BASE 0xAD000000 /* PCIe Port 0 CFG Type 0 */ - #define CNS3XXX_PCIE0_CFG0_BASE_VIRT 0xE3000000 -@@ -175,13 +177,16 @@ - #define CNS3XXX_PCIE0_CFG1_BASE_VIRT 0xE4000000 - - #define CNS3XXX_PCIE0_MSG_BASE 0xAF000000 /* PCIe Port 0 Message Space */ -+#define CNS3XXX_PCIE0_MSG_BASE_VIRT 0xE5000000 - - #define CNS3XXX_PCIE1_MEM_BASE 0xB0000000 /* PCIe Port 1 IO/Memory Space */ -+#define CNS3XXX_PCIE1_MEM_BASE_VIRT 0xE8000000 - - #define CNS3XXX_PCIE1_HOST_BASE 0xBB000000 /* PCIe Port 1 RC Base */ - #define CNS3XXX_PCIE1_HOST_BASE_VIRT 0xE9000000 - - #define CNS3XXX_PCIE1_IO_BASE 0xBC000000 /* PCIe Port 1 */ -+#define CNS3XXX_PCIE1_IO_BASE_VIRT 0xEA000000 - - #define CNS3XXX_PCIE1_CFG0_BASE 0xBD000000 /* PCIe Port 1 CFG Type 0 */ - #define CNS3XXX_PCIE1_CFG0_BASE_VIRT 0xEB000000 -@@ -190,6 +195,7 @@ - #define CNS3XXX_PCIE1_CFG1_BASE_VIRT 0xEC000000 - - #define CNS3XXX_PCIE1_MSG_BASE 0xBF000000 /* PCIe Port 1 Message Space */ -+#define CNS3XXX_PCIE1_MSG_BASE_VIRT 0xED000000 - - /* - * Testchip peripheral and fpga gic regions diff --git a/target/linux/cns3xxx/patches-4.19/095-gpio_support.patch b/target/linux/cns3xxx/patches-4.19/095-gpio_support.patch deleted file mode 100644 index 8345f2f144..0000000000 --- a/target/linux/cns3xxx/patches-4.19/095-gpio_support.patch +++ /dev/null @@ -1,67 +0,0 @@ ---- a/arch/arm/mach-cns3xxx/cns3420vb.c -+++ b/arch/arm/mach-cns3xxx/cns3420vb.c -@@ -223,6 +223,10 @@ static void __init cns3420_init(void) - - cns3xxx_ahci_init(); - cns3xxx_sdhci_init(); -+ cns3xxx_gpio_init( 0, 32, CNS3XXX_GPIOA_BASE_VIRT, IRQ_CNS3XXX_GPIOA, -+ NR_IRQS_CNS3XXX); -+ cns3xxx_gpio_init(32, 32, CNS3XXX_GPIOB_BASE_VIRT, IRQ_CNS3XXX_GPIOB, -+ NR_IRQS_CNS3XXX + 32); - - pm_power_off = cns3xxx_power_off; - } ---- a/arch/arm/mach-cns3xxx/Kconfig -+++ b/arch/arm/mach-cns3xxx/Kconfig -@@ -3,6 +3,8 @@ menuconfig ARCH_CNS3XXX - bool "Cavium Networks CNS3XXX family" - depends on ARCH_MULTI_V6 - select ARM_GIC -+ select ARCH_REQUIRE_GPIOLIB -+ select GENERIC_IRQ_CHIP - select HAVE_ARM_SCU if SMP - select HAVE_ARM_TWD - select HAVE_SMP ---- a/arch/arm/mach-cns3xxx/Makefile -+++ b/arch/arm/mach-cns3xxx/Makefile -@@ -2,7 +2,7 @@ - ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/$(src)/include - - obj-$(CONFIG_ARCH_CNS3XXX) += cns3xxx.o --cns3xxx-y += core.o pm.o -+cns3xxx-y += core.o pm.o gpio.o - cns3xxx-$(CONFIG_ATAGS) += devices.o - cns3xxx-$(CONFIG_PCI) += pcie.o - cns3xxx-$(CONFIG_MACH_CNS3420VB) += cns3420vb.o ---- a/arch/arm/mach-cns3xxx/cns3xxx.h -+++ b/arch/arm/mach-cns3xxx/cns3xxx.h -@@ -68,8 +68,10 @@ - #define SMC_PCELL_ID_3_OFFSET 0xFFC - - #define CNS3XXX_GPIOA_BASE 0x74000000 /* GPIO port A */ -+#define CNS3XXX_GPIOA_BASE_VIRT 0xFB006000 - - #define CNS3XXX_GPIOB_BASE 0x74800000 /* GPIO port B */ -+#define CNS3XXX_GPIOB_BASE_VIRT 0xFB007000 - - #define CNS3XXX_RTC_BASE 0x75000000 /* Real Time Clock */ - ---- a/arch/arm/mach-cns3xxx/core.c -+++ b/arch/arm/mach-cns3xxx/core.c -@@ -50,6 +50,16 @@ static struct map_desc cns3xxx_io_desc[] - .pfn = __phys_to_pfn(CNS3XXX_PM_BASE), - .length = SZ_4K, - .type = MT_DEVICE, -+ }, { -+ .virtual = CNS3XXX_GPIOA_BASE_VIRT, -+ .pfn = __phys_to_pfn(CNS3XXX_GPIOA_BASE), -+ .length = SZ_4K, -+ .type = MT_DEVICE, -+ }, { -+ .virtual = CNS3XXX_GPIOB_BASE_VIRT, -+ .pfn = __phys_to_pfn(CNS3XXX_GPIOB_BASE), -+ .length = SZ_4K, -+ .type = MT_DEVICE, - #ifdef CONFIG_PCI - }, { - .virtual = CNS3XXX_PCIE0_HOST_BASE_VIRT, diff --git a/target/linux/cns3xxx/patches-4.19/097-l2x0_cmdline_disable.patch b/target/linux/cns3xxx/patches-4.19/097-l2x0_cmdline_disable.patch deleted file mode 100644 index b4720b11de..0000000000 --- a/target/linux/cns3xxx/patches-4.19/097-l2x0_cmdline_disable.patch +++ /dev/null @@ -1,69 +0,0 @@ ---- a/arch/arm/mach-cns3xxx/core.c -+++ b/arch/arm/mach-cns3xxx/core.c -@@ -307,13 +307,26 @@ void __init cns3xxx_timer_init(void) - - #ifdef CONFIG_CACHE_L2X0 - --void __init cns3xxx_l2x0_init(void) -+static int cns3xxx_l2x0_enable = 1; -+ -+static int __init cns3xxx_l2x0_disable(char *s) -+{ -+ cns3xxx_l2x0_enable = 0; -+ return 1; -+} -+__setup("nol2x0", cns3xxx_l2x0_disable); -+ -+static int __init cns3xxx_l2x0_init(void) - { -- void __iomem *base = ioremap(CNS3XXX_L2C_BASE, SZ_4K); -+ void __iomem *base; - u32 val; - -+ if (!cns3xxx_l2x0_enable) -+ return 0; -+ -+ base = ioremap(CNS3XXX_L2C_BASE, SZ_4K); - if (WARN_ON(!base)) -- return; -+ return 0; - - /* - * Tag RAM Control register -@@ -343,7 +356,10 @@ void __init cns3xxx_l2x0_init(void) - - /* 32 KiB, 8-way, parity disable */ - l2x0_init(base, 0x00500000, 0xfe0f0fff); -+ -+ return 0; - } -+arch_initcall(cns3xxx_l2x0_init); - - #endif /* CONFIG_CACHE_L2X0 */ - ---- a/arch/arm/mach-cns3xxx/cns3420vb.c -+++ b/arch/arm/mach-cns3xxx/cns3420vb.c -@@ -217,8 +217,6 @@ static struct platform_device *cns3420_p - - static void __init cns3420_init(void) - { -- cns3xxx_l2x0_init(); -- - platform_add_devices(cns3420_pdevs, ARRAY_SIZE(cns3420_pdevs)); - - cns3xxx_ahci_init(); ---- a/arch/arm/mach-cns3xxx/core.h -+++ b/arch/arm/mach-cns3xxx/core.h -@@ -16,12 +16,6 @@ - extern struct smp_operations cns3xxx_smp_ops; - extern void cns3xxx_timer_init(void); - --#ifdef CONFIG_CACHE_L2X0 --void __init cns3xxx_l2x0_init(void); --#else --static inline void cns3xxx_l2x0_init(void) {} --#endif /* CONFIG_CACHE_L2X0 */ -- - #ifdef CONFIG_PCI - extern void __init cns3xxx_pcie_init_late(void); - #else diff --git a/target/linux/cns3xxx/patches-4.19/100-laguna_support.patch b/target/linux/cns3xxx/patches-4.19/100-laguna_support.patch deleted file mode 100644 index c0160d8f12..0000000000 --- a/target/linux/cns3xxx/patches-4.19/100-laguna_support.patch +++ /dev/null @@ -1,46 +0,0 @@ ---- a/arch/arm/mach-cns3xxx/Kconfig -+++ b/arch/arm/mach-cns3xxx/Kconfig -@@ -23,4 +23,12 @@ config MACH_CNS3420VB - This is a platform with an on-board ARM11 MPCore and has support - for USB, USB-OTG, MMC/SD/SDIO, SATA, PCI-E, etc. - -+config MACH_GW2388 -+ bool "Support for Gateworks Laguna Platform" -+ help -+ Include support for the Gateworks Laguna Platform -+ -+ This is a platform with an on-board ARM11 MPCore and has support -+ for USB, USB-OTG, MMC/SD/SDIO, SATA, PCI-E, I2C, GIG, etc. -+ - endif ---- a/arch/arm/mach-cns3xxx/Makefile -+++ b/arch/arm/mach-cns3xxx/Makefile -@@ -8,3 +8,5 @@ cns3xxx-$(CONFIG_PCI) += pcie.o - cns3xxx-$(CONFIG_MACH_CNS3420VB) += cns3420vb.o - cns3xxx-$(CONFIG_SMP) += platsmp.o headsmp.o cns3xxx_fiq.o - cns3xxx-$(CONFIG_HOTPLUG_CPU) += hotplug.o -+cns3xxx-$(CONFIG_MACH_GW2388) += laguna.o -+ ---- a/arch/arm/mach-cns3xxx/devices.c -+++ b/arch/arm/mach-cns3xxx/devices.c -@@ -16,6 +16,7 @@ - #include - #include - #include -+#include - #include "cns3xxx.h" - #include "pm.h" - #include "core.h" -@@ -101,7 +102,11 @@ void __init cns3xxx_sdhci_init(void) - u32 gpioa_pins = __raw_readl(gpioa); - - /* MMC/SD pins share with GPIOA */ -- gpioa_pins |= 0x1fff0004; -+ if (machine_is_gw2388()) { -+ gpioa_pins |= 0x1fff0000; -+ } else { -+ gpioa_pins |= 0x1fff0004; -+ } - __raw_writel(gpioa_pins, gpioa); - - cns3xxx_pwr_clk_en(CNS3XXX_PWR_CLK_EN(SDIO)); diff --git a/target/linux/cns3xxx/patches-4.19/101-laguna_sdhci_card_detect.patch b/target/linux/cns3xxx/patches-4.19/101-laguna_sdhci_card_detect.patch deleted file mode 100644 index 72648a5f9f..0000000000 --- a/target/linux/cns3xxx/patches-4.19/101-laguna_sdhci_card_detect.patch +++ /dev/null @@ -1,14 +0,0 @@ ---- a/drivers/mmc/host/sdhci-cns3xxx.c -+++ b/drivers/mmc/host/sdhci-cns3xxx.c -@@ -88,9 +88,9 @@ static const struct sdhci_pltfm_data sdh - .ops = &sdhci_cns3xxx_ops, - .quirks = SDHCI_QUIRK_BROKEN_DMA | - SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | -- SDHCI_QUIRK_INVERTED_WRITE_PROTECT | - SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | -- SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, -+ SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | -+ SDHCI_QUIRK_BROKEN_CARD_DETECTION, - }; - - static int sdhci_cns3xxx_probe(struct platform_device *pdev) diff --git a/target/linux/cns3xxx/patches-4.19/110-pci_isolated_interrupts.patch b/target/linux/cns3xxx/patches-4.19/110-pci_isolated_interrupts.patch deleted file mode 100644 index 800be813b8..0000000000 --- a/target/linux/cns3xxx/patches-4.19/110-pci_isolated_interrupts.patch +++ /dev/null @@ -1,95 +0,0 @@ ---- a/arch/arm/mach-cns3xxx/pcie.c -+++ b/arch/arm/mach-cns3xxx/pcie.c -@@ -18,6 +18,7 @@ - #include - #include - #include -+#include - #include - #include - #include "cns3xxx.h" -@@ -27,7 +28,7 @@ struct cns3xxx_pcie { - void __iomem *host_regs; /* PCI config registers for host bridge */ - void __iomem *cfg0_regs; /* PCI Type 0 config registers */ - void __iomem *cfg1_regs; /* PCI Type 1 config registers */ -- unsigned int irqs[2]; -+ unsigned int irqs[5]; - struct resource res_io; - struct resource res_mem; - int port; -@@ -95,7 +96,7 @@ static inline int check_master_abort(str - void __iomem *host_base; - u32 sreg, ereg; - -- host_base = (void __iomem *) cnspci->cfg_bases[CNS3XXX_HOST_TYPE].virtual; -+ host_base = (void __iomem *) cnspci->host_regs; - sreg = __raw_readw(host_base + 0x6) & 0xF900; - ereg = __raw_readl(host_base + 0x104); // Uncorrectable Error Status Reg - -@@ -209,7 +210,7 @@ static struct pci_ops cns3xxx_pcie_ops = - static int cns3xxx_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) - { - struct cns3xxx_pcie *cnspci = pdev_to_cnspci(dev); -- int irq = cnspci->irqs[!!dev->bus->number]; -+ int irq = cnspci->irqs[!!dev->bus->number + pin - 1]; - - pr_info("PCIe map irq: %04d:%02x:%02x.%02x slot %d, pin %d, irq: %d\n", - pci_domain_nr(dev->bus), dev->bus->number, PCI_SLOT(dev->devfn), -@@ -235,7 +236,13 @@ static struct cns3xxx_pcie cns3xxx_pcie[ - .end = CNS3XXX_PCIE0_HOST_BASE - 1, /* 176 MiB */ - .flags = IORESOURCE_MEM, - }, -- .irqs = { IRQ_CNS3XXX_PCIE0_RC, IRQ_CNS3XXX_PCIE0_DEVICE, }, -+ .irqs = { -+ IRQ_CNS3XXX_PCIE0_RC, -+ IRQ_CNS3XXX_PCIE0_DEVICE, -+ IRQ_CNS3XXX_PCIE0_DEVICE, -+ IRQ_CNS3XXX_PCIE0_DEVICE, -+ IRQ_CNS3XXX_PCIE0_DEVICE, -+ }, - .port = 0, - }, - [1] = { -@@ -254,7 +261,13 @@ static struct cns3xxx_pcie cns3xxx_pcie[ - .end = CNS3XXX_PCIE1_HOST_BASE - 1, /* 176 MiB */ - .flags = IORESOURCE_MEM, - }, -- .irqs = { IRQ_CNS3XXX_PCIE1_RC, IRQ_CNS3XXX_PCIE1_DEVICE, }, -+ .irqs = { -+ IRQ_CNS3XXX_PCIE1_RC, -+ IRQ_CNS3XXX_PCIE1_DEVICE, -+ IRQ_CNS3XXX_PCIE1_DEVICE, -+ IRQ_CNS3XXX_PCIE1_DEVICE, -+ IRQ_CNS3XXX_PCIE1_DEVICE, -+ }, - .port = 1, - }, - }; -@@ -346,6 +359,14 @@ static int cns3xxx_pcie_abort_handler(un - return 0; - } - -+void __init cns3xxx_pcie_set_irqs(int bus, int *irqs) -+{ -+ int i; -+ -+ for (i = 0; i < 4; i++) -+ cns3xxx_pcie[bus].irqs[i + 1] = irqs[i]; -+} -+ - void __init cns3xxx_pcie_init_late(void) - { - int i; ---- a/arch/arm/mach-cns3xxx/core.h -+++ b/arch/arm/mach-cns3xxx/core.h -@@ -18,8 +18,10 @@ extern void cns3xxx_timer_init(void); - - #ifdef CONFIG_PCI - extern void __init cns3xxx_pcie_init_late(void); -+extern void __init cns3xxx_pcie_set_irqs(int bus, int *irqs); - #else - static inline void __init cns3xxx_pcie_init_late(void) {} -+static inline void cns3xxx_pcie_set_irqs(int bus, int *irqs) {} - #endif - - void __init cns3xxx_map_io(void); diff --git a/target/linux/cns3xxx/patches-4.19/130-Extend-PCIE_BUS_PEER2PEER-to-set-MRSS-128-to-fix-CNS3xxx-BM-DMA..patch b/target/linux/cns3xxx/patches-4.19/130-Extend-PCIE_BUS_PEER2PEER-to-set-MRSS-128-to-fix-CNS3xxx-BM-DMA..patch deleted file mode 100644 index 67aa8df7fe..0000000000 --- a/target/linux/cns3xxx/patches-4.19/130-Extend-PCIE_BUS_PEER2PEER-to-set-MRSS-128-to-fix-CNS3xxx-BM-DMA..patch +++ /dev/null @@ -1,23 +0,0 @@ ---- a/drivers/pci/probe.c -+++ b/drivers/pci/probe.c -@@ -2655,7 +2655,8 @@ static void pcie_write_mrrs(struct pci_d - * In the "safe" case, do not configure the MRRS. There appear to be - * issues with setting MRRS to 0 on a number of devices. - */ -- if (pcie_bus_config != PCIE_BUS_PERFORMANCE) -+ if (pcie_bus_config != PCIE_BUS_PERFORMANCE && -+ pcie_bus_config != PCIE_BUS_PEER2PEER) - return; - - /* ---- a/include/linux/pci.h -+++ b/include/linux/pci.h -@@ -868,7 +868,7 @@ enum pcie_bus_config_types { - PCIE_BUS_DEFAULT, /* Ensure MPS matches upstream bridge */ - PCIE_BUS_SAFE, /* Use largest MPS boot-time devices support */ - PCIE_BUS_PERFORMANCE, /* Use MPS and MRRS for best performance */ -- PCIE_BUS_PEER2PEER, /* Set MPS = 128 for all devices */ -+ PCIE_BUS_PEER2PEER, /* set MPS and MRSS to 128 for all devices */ - }; - - extern enum pcie_bus_config_types pcie_bus_config; diff --git a/target/linux/cns3xxx/patches-4.19/200-broadcom_phy_reinit.patch b/target/linux/cns3xxx/patches-4.19/200-broadcom_phy_reinit.patch deleted file mode 100644 index b0c3299d85..0000000000 --- a/target/linux/cns3xxx/patches-4.19/200-broadcom_phy_reinit.patch +++ /dev/null @@ -1,15 +0,0 @@ ---- a/drivers/net/phy/broadcom.c -+++ b/drivers/net/phy/broadcom.c -@@ -429,7 +429,11 @@ static int bcm5481_config_aneg(struct ph - ret = genphy_config_aneg(phydev); - - /* Then we can set up the delay. */ -- bcm5481x_config(phydev); -+ //bcm5481x_config(phydev); -+ -+ phy_write(phydev, 0x18, 0xf1e7); -+ phy_write(phydev, 0x1c, 0x8e00); -+ phy_write(phydev, 0x1c, 0xa41f); - - if (of_property_read_bool(np, "enet-phy-lane-swap")) { - /* Lane Swap - Undocumented register...magic! */ diff --git a/target/linux/cns3xxx/patches-4.19/210-dwc2_defaults.patch b/target/linux/cns3xxx/patches-4.19/210-dwc2_defaults.patch deleted file mode 100644 index 3efcc18854..0000000000 --- a/target/linux/cns3xxx/patches-4.19/210-dwc2_defaults.patch +++ /dev/null @@ -1,63 +0,0 @@ ---- a/drivers/usb/dwc2/params.c -+++ b/drivers/usb/dwc2/params.c -@@ -152,6 +152,36 @@ static void dwc2_set_stm32f7_hsotg_param - p->host_perio_tx_fifo_size = 256; - } - -+static void dwc2_set_cns3xxx_params(struct dwc2_hsotg *hsotg) -+{ -+ struct dwc2_core_params *p = &hsotg->params; -+ -+ p->otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE; /* non-HNP/non-SRP capable */ -+ p->host_dma = 1; -+ p->dma_desc_enable = 0; -+ p->speed = DWC2_SPEED_PARAM_HIGH; /* High Speed */ -+ p->enable_dynamic_fifo = 1; -+ p->en_multiple_tx_fifo = 1; -+ p->host_rx_fifo_size = 658; /* 774 DWORDs */ -+ p->host_nperio_tx_fifo_size = 128; /* 256 DWORDs */ -+ p->host_perio_tx_fifo_size = 658; /* 512 DWORDs */ -+ p->max_transfer_size = 65535, -+ p->max_packet_count = 511; -+ p->host_channels = 16; -+ p->phy_type = DWC2_PHY_TYPE_PARAM_UTMI; /* UTMI */ -+ p->phy_utmi_width = 16; /* 8 bits */ -+ p->phy_ulpi_ddr = 0; /* Single */ -+ p->phy_ulpi_ext_vbus = 0; -+ p->i2c_enable = 0; -+ p->ulpi_fs_ls = 0; -+ p->host_support_fs_ls_low_power = 0; -+ p->host_ls_low_power_phy_clk = 0; /* 48 MHz */ -+ p->ts_dline = 0; -+ p->reload_ctl = 0; -+ p->ahbcfg = 0x10; -+ p->uframe_sched = false; -+} -+ - const struct of_device_id dwc2_of_match_table[] = { - { .compatible = "brcm,bcm2835-usb", .data = dwc2_set_bcm_params }, - { .compatible = "hisilicon,hi6220-usb", .data = dwc2_set_his_params }, -@@ -820,17 +850,23 @@ int dwc2_get_hwparams(struct dwc2_hsotg - - int dwc2_init_params(struct dwc2_hsotg *hsotg) - { -+ /* - const struct of_device_id *match; - void (*set_params)(void *data); -+ */ - - dwc2_set_default_params(hsotg); - dwc2_get_device_properties(hsotg); - -+ /* - match = of_match_device(dwc2_of_match_table, hsotg->dev); - if (match && match->data) { - set_params = match->data; - set_params(hsotg); - } -+ */ -+ -+ dwc2_set_cns3xxx_params(hsotg); - - dwc2_check_params(hsotg); - diff --git a/target/linux/mediatek/patches-5.4/1000-eth-gdm-config-backport.patch b/target/linux/generic/backport-5.4/760-net-ethernet-mediatek-Integrate-GDM-PSE-setup-operat.patch similarity index 65% rename from target/linux/mediatek/patches-5.4/1000-eth-gdm-config-backport.patch rename to target/linux/generic/backport-5.4/760-net-ethernet-mediatek-Integrate-GDM-PSE-setup-operat.patch index a2acadfd90..4df1fd2677 100644 --- a/target/linux/mediatek/patches-5.4/1000-eth-gdm-config-backport.patch +++ b/target/linux/generic/backport-5.4/760-net-ethernet-mediatek-Integrate-GDM-PSE-setup-operat.patch @@ -1,6 +1,16 @@ +From: MarkLee +Date: Wed, 13 Nov 2019 10:38:42 +0800 +Subject: [PATCH] net: ethernet: mediatek: Integrate GDM/PSE setup operations + +Integrate GDM/PSE setup operations into single function "mtk_gdm_config" + +Signed-off-by: MarkLee +Signed-off-by: David S. Miller +--- + --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -2240,6 +2240,31 @@ static int mtk_start_dma(struct mtk_eth +@@ -2211,6 +2211,28 @@ static int mtk_start_dma(struct mtk_eth return 0; } @@ -8,9 +18,6 @@ +{ + int i; + -+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) -+ return; -+ + for (i = 0; i < MTK_MAC_COUNT; i++) { + u32 val = mtk_r32(eth, MTK_GDMA_FWD_CFG(i)); + @@ -32,26 +39,8 @@ static int mtk_open(struct net_device *dev) { struct mtk_mac *mac = netdev_priv(dev); -@@ -2260,6 +2285,8 @@ static int mtk_open(struct net_device *d - if (err) - return err; - -+ mtk_gdm_config(eth, MTK_GDMA_TO_PDMA); -+ - napi_enable(ð->tx_napi); - napi_enable(ð->rx_napi); - mtk_tx_irq_enable(eth, MTK_TX_DONE_INT); -@@ -2315,6 +2342,8 @@ static int mtk_stop(struct net_device *d - if (!refcount_dec_and_test(ð->dma_refcnt)) - return 0; - -+ mtk_gdm_config(eth, MTK_GDMA_DROP_ALL); -+ - mtk_tx_irq_disable(eth, MTK_TX_DONE_INT); - mtk_rx_irq_disable(eth, MTK_RX_DONE_INT); - napi_disable(ð->tx_napi); -@@ -2498,8 +2527,6 @@ static int mtk_hw_init(struct mtk_eth *e - /* disable delay and normal interrupt */ +@@ -2406,8 +2428,6 @@ static int mtk_hw_init(struct mtk_eth *e + mtk_w32(eth, 0, MTK_QDMA_DELAY_INT); mtk_tx_irq_disable(eth, ~0); mtk_rx_irq_disable(eth, ~0); - mtk_w32(eth, RST_GL_PSE, MTK_RST_GL); @@ -59,7 +48,7 @@ /* FE int grouping */ mtk_w32(eth, MTK_TX_DONE_INT, MTK_PDMA_INT_GRP1); -@@ -2508,19 +2535,6 @@ static int mtk_hw_init(struct mtk_eth *e +@@ -2416,18 +2436,7 @@ static int mtk_hw_init(struct mtk_eth *e mtk_w32(eth, MTK_RX_DONE_INT, MTK_QDMA_INT_GRP2); mtk_w32(eth, 0x21021000, MTK_FE_INT_GRP); @@ -75,18 +64,17 @@ - /* setup the mac dma */ - mtk_w32(eth, val, MTK_GDMA_FWD_CFG(i)); - } -- ++ mtk_gdm_config(eth, MTK_GDMA_TO_PDMA); + return 0; - err_disable_pm: --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -85,6 +85,8 @@ +@@ -84,6 +84,7 @@ #define MTK_GDMA_ICS_EN BIT(22) #define MTK_GDMA_TCS_EN BIT(21) #define MTK_GDMA_UCS_EN BIT(20) +#define MTK_GDMA_TO_PDMA 0x0 -+#define MTK_GDMA_DROP_ALL 0x7777 /* Unicast Filter MAC Address Register - Low */ #define MTK_GDMA_MAC_ADRL(x) (0x508 + (x * 0x1000)) diff --git a/target/linux/generic/backport-5.4/761-net-ethernet-mediatek-Refine-the-timing-of-GDM-PSE-s.patch b/target/linux/generic/backport-5.4/761-net-ethernet-mediatek-Refine-the-timing-of-GDM-PSE-s.patch new file mode 100644 index 0000000000..cdcd6a1a33 --- /dev/null +++ b/target/linux/generic/backport-5.4/761-net-ethernet-mediatek-Refine-the-timing-of-GDM-PSE-s.patch @@ -0,0 +1,45 @@ +From: MarkLee +Date: Wed, 13 Nov 2019 10:38:43 +0800 +Subject: [PATCH] net: ethernet: mediatek: Refine the timing of GDM/PSE setup + +Refine the timing of GDM/PSE setup, move it from mtk_hw_init +to mtk_open. This is recommended by the mt762x HW design to +do GDM/PSE setup only after PDMA has been started. + +We exclude mt7628 in mtk_gdm_config function since it is a old IP +and there is no GDM/PSE block on it. + +Signed-off-by: MarkLee +Signed-off-by: David S. Miller +--- + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -2215,6 +2215,9 @@ static void mtk_gdm_config(struct mtk_et + { + int i; + ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) ++ return; ++ + for (i = 0; i < MTK_MAC_COUNT; i++) { + u32 val = mtk_r32(eth, MTK_GDMA_FWD_CFG(i)); + +@@ -2253,6 +2256,8 @@ static int mtk_open(struct net_device *d + if (err) + return err; + ++ mtk_gdm_config(eth, MTK_GDMA_TO_PDMA); ++ + napi_enable(ð->tx_napi); + napi_enable(ð->rx_napi); + mtk_tx_irq_enable(eth, MTK_TX_DONE_INT); +@@ -2436,8 +2441,6 @@ static int mtk_hw_init(struct mtk_eth *e + mtk_w32(eth, MTK_RX_DONE_INT, MTK_QDMA_INT_GRP2); + mtk_w32(eth, 0x21021000, MTK_FE_INT_GRP); + +- mtk_gdm_config(eth, MTK_GDMA_TO_PDMA); +- + return 0; + + err_disable_pm: diff --git a/target/linux/generic/backport-5.4/762-net-ethernet-mediatek-Enable-GDM-GDMA_DROP_ALL-mode.patch b/target/linux/generic/backport-5.4/762-net-ethernet-mediatek-Enable-GDM-GDMA_DROP_ALL-mode.patch new file mode 100644 index 0000000000..b8c40ef665 --- /dev/null +++ b/target/linux/generic/backport-5.4/762-net-ethernet-mediatek-Enable-GDM-GDMA_DROP_ALL-mode.patch @@ -0,0 +1,33 @@ +From: MarkLee +Date: Wed, 13 Nov 2019 10:38:44 +0800 +Subject: [PATCH] net: ethernet: mediatek: Enable GDM GDMA_DROP_ALL mode + +Enable GDM GDMA_DROP_ALL mode to drop all packet during the +stop operation. This is recommended by the mt762x HW design +to drop all packet from GMAC before stopping PDMA. + +Signed-off-by: MarkLee +Signed-off-by: David S. Miller +--- + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -2310,6 +2310,8 @@ static int mtk_stop(struct net_device *d + if (!refcount_dec_and_test(ð->dma_refcnt)) + return 0; + ++ mtk_gdm_config(eth, MTK_GDMA_DROP_ALL); ++ + mtk_tx_irq_disable(eth, MTK_TX_DONE_INT); + mtk_rx_irq_disable(eth, MTK_RX_DONE_INT); + napi_disable(ð->tx_napi); +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -85,6 +85,7 @@ + #define MTK_GDMA_TCS_EN BIT(21) + #define MTK_GDMA_UCS_EN BIT(20) + #define MTK_GDMA_TO_PDMA 0x0 ++#define MTK_GDMA_DROP_ALL 0x7777 + + /* Unicast Filter MAC Address Register - Low */ + #define MTK_GDMA_MAC_ADRL(x) (0x508 + (x * 0x1000)) diff --git a/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_uimage.c b/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_uimage.c index 7a9ec8acde..281175bae7 100644 --- a/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_uimage.c +++ b/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_uimage.c @@ -319,8 +319,57 @@ static struct mtd_part_parser uimage_netgear_parser = { .of_match_table = mtdsplit_uimage_netgear_of_match_table, .parse_fn = mtdsplit_uimage_parse_netgear, .type = MTD_PARSER_TYPE_FIRMWARE, + }; + +/************************************************** + * ALLNET + **************************************************/ + +#define FW_MAGIC_SG8208M 0x00000006 +#define FW_MAGIC_SG8310PM 0x83000006 + +static ssize_t uimage_verify_allnet(u_char *buf, size_t len, int *extralen) +{ + struct uimage_header *header = (struct uimage_header *)buf; + + switch (be32_to_cpu(header->ih_magic)) { + case FW_MAGIC_SG8208M: + case FW_MAGIC_SG8310PM: + break; + default: + return -EINVAL; + } + + if (header->ih_os != IH_OS_LINUX) + return -EINVAL; + + return 0; +} + +static int +mtdsplit_uimage_parse_allnet(struct mtd_info *master, + const struct mtd_partition **pparts, + struct mtd_part_parser_data *data) +{ + return __mtdsplit_parse_uimage(master, pparts, data, + uimage_verify_allnet); +} + +static const struct of_device_id mtdsplit_uimage_allnet_of_match_table[] = { + { .compatible = "allnet,uimage" }, + {}, +}; + +static struct mtd_part_parser uimage_allnet_parser = { + .owner = THIS_MODULE, + .name = "allnet-fw", + .of_match_table = mtdsplit_uimage_allnet_of_match_table, + .parse_fn = mtdsplit_uimage_parse_allnet, +}; + + /************************************************** * Edimax **************************************************/ @@ -505,6 +554,7 @@ static int __init mtdsplit_uimage_init(void) { register_mtd_parser(&uimage_generic_parser); register_mtd_parser(&uimage_netgear_parser); + register_mtd_parser(&uimage_allnet_parser); register_mtd_parser(&uimage_edimax_parser); register_mtd_parser(&uimage_fonfxc_parser); register_mtd_parser(&uimage_sge_parser); diff --git a/target/linux/generic/hack-4.14/952-net-conntrack-events-support-multiple-registrant.patch b/target/linux/generic/hack-4.14/952-net-conntrack-events-support-multiple-registrant.patch deleted file mode 100644 index f844328dbe..0000000000 --- a/target/linux/generic/hack-4.14/952-net-conntrack-events-support-multiple-registrant.patch +++ /dev/null @@ -1,325 +0,0 @@ -diff --git a/include/net/netfilter/nf_conntrack_ecache.h b/include/net/netfilter/nf_conntrack_ecache.h -index 12d967b..c2b98b6 100644 ---- a/include/net/netfilter/nf_conntrack_ecache.h -+++ b/include/net/netfilter/nf_conntrack_ecache.h -@@ -71,6 +71,10 @@ struct nf_ct_event { - int report; - }; - -+#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS -+extern int nf_conntrack_register_notifier(struct net *net, struct notifier_block *nb); -+extern int nf_conntrack_unregister_notifier(struct net *net, struct notifier_block *nb); -+#else - struct nf_ct_event_notifier { - int (*fcn)(unsigned int events, struct nf_ct_event *item); - }; -@@ -79,7 +83,7 @@ int nf_conntrack_register_notifier(struc - struct nf_ct_event_notifier *nb); - void nf_conntrack_unregister_notifier(struct net *net, - struct nf_ct_event_notifier *nb); -- -+#endif - void nf_ct_deliver_cached_events(struct nf_conn *ct); - int nf_conntrack_eventmask_report(unsigned int eventmask, struct nf_conn *ct, - u32 portid, int report); -@@ -87,12 +91,15 @@ int nf_conntrack_eventmask_report(unsign - static inline void - nf_conntrack_event_cache(enum ip_conntrack_events event, struct nf_conn *ct) - { -- struct net *net = nf_ct_net(ct); - struct nf_conntrack_ecache *e; -+#ifndef CONFIG_NF_CONNTRACK_CHAIN_EVENTS -+ struct net *net = nf_ct_net(ct); - - if (!rcu_access_pointer(net->ct.nf_conntrack_event_cb)) - return; - -+#endif -+ - e = nf_ct_ecache_find(ct); - if (e == NULL) - return; -@@ -104,10 +111,12 @@ static inline int - nf_conntrack_event_report(enum ip_conntrack_events event, struct nf_conn *ct, - u32 portid, int report) - { -+#ifndef CONFIG_NF_CONNTRACK_CHAIN_EVENTS - const struct net *net = nf_ct_net(ct); - - if (!rcu_access_pointer(net->ct.nf_conntrack_event_cb)) - return 0; -+#endif - - return nf_conntrack_eventmask_report(1 << event, ct, portid, report); - } -@@ -115,11 +124,14 @@ nf_conntrack_event_report(enum ip_conntr - static inline int - nf_conntrack_event(enum ip_conntrack_events event, struct nf_conn *ct) - { -+#ifndef CONFIG_NF_CONNTRACK_CHAIN_EVENTS - const struct net *net = nf_ct_net(ct); - - if (!rcu_access_pointer(net->ct.nf_conntrack_event_cb)) - return 0; - -+#endif -+ - return nf_conntrack_eventmask_report(1 << event, ct, 0, 0); - } - -diff --git a/include/net/netns/conntrack.h b/include/net/netns/conntrack.h -index e469e85..1d31db8 100644 ---- a/include/net/netns/conntrack.h -+++ b/include/net/netns/conntrack.h -@@ -114,7 +114,11 @@ struct netns_ct { - - struct ct_pcpu __percpu *pcpu_lists; - struct ip_conntrack_stat __percpu *stat; -+#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS -+ struct atomic_notifier_head nf_conntrack_chain; -+#else - struct nf_ct_event_notifier __rcu *nf_conntrack_event_cb; -+#endif - struct nf_exp_event_notifier __rcu *nf_expect_event_cb; - struct nf_ip_net nf_ct_proto; - #if defined(CONFIG_NF_CONNTRACK_LABELS) -diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig -index 63073be..08d7aab 100644 ---- a/net/netfilter/Kconfig -+++ b/net/netfilter/Kconfig -@@ -118,6 +118,14 @@ config NF_CONNTRACK_EVENTS - - If unsure, say `N'. - -+config NF_CONNTRACK_CHAIN_EVENTS -+ bool "Register multiple callbacks to ct events" -+ depends on NF_CONNTRACK_EVENTS -+ help -+ Support multiple registrations. -+ -+ If unsure, say `N'. -+ - config NF_CONNTRACK_TIMEOUT - bool 'Connection tracking timeout' - depends on NETFILTER_ADVANCED -diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c -index 6bd1508..9b81c7c 100644 ---- a/net/netfilter/nf_conntrack_core.c -+++ b/net/netfilter/nf_conntrack_core.c -@@ -2167,6 +2167,10 @@ int nf_conntrack_init_net(struct net *ne - ret = nf_conntrack_proto_pernet_init(net); - if (ret < 0) - goto err_proto; -+ -+#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS -+ ATOMIC_INIT_NOTIFIER_HEAD(&net->ct.nf_conntrack_chain); -+#endif - return 0; - - err_proto: -diff --git a/net/netfilter/nf_conntrack_ecache.c b/net/netfilter/nf_conntrack_ecache.c -index da9df2d..e0e2a8f 100644 ---- a/net/netfilter/nf_conntrack_ecache.c -+++ b/net/netfilter/nf_conntrack_ecache.c -@@ -18,6 +18,9 @@ - #include - #include - #include -+#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS -+#include -+#endif - #include - #include - #include -@@ -117,6 +120,38 @@ static void ecache_work(struct work_stru - schedule_delayed_work(&ctnet->ecache_dwork, delay); - } - -+#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS -+int -+nf_conntrack_eventmask_report(unsigned int eventmask, -+ struct nf_conn *ct, -+ u32 portid, -+ int report) -+{ -+ struct nf_conntrack_ecache *e; -+ struct net *net = nf_ct_net(ct); -+ -+ e = nf_ct_ecache_find(ct); -+ if (e == NULL) -+ return 0; -+ -+ if (nf_ct_is_confirmed(ct)) { -+ struct nf_ct_event item = { -+ .ct = ct, -+ .portid = e->portid ? e->portid : portid, -+ .report = report -+ }; -+ /* This is a resent of a destroy event? If so, skip missed */ -+ unsigned long missed = e->portid ? 0 : e->missed; -+ -+ if (!((eventmask | missed) & e->ctmask)) -+ return 0; -+ -+ atomic_notifier_call_chain(&net->ct.nf_conntrack_chain, eventmask | missed, &item); -+ } -+ -+ return 0; -+} -+#else - int nf_conntrack_eventmask_report(unsigned int eventmask, struct nf_conn *ct, - u32 portid, int report) - { -@@ -171,10 +206,52 @@ out_unlock: - rcu_read_unlock(); - return ret; - } -+#endif - EXPORT_SYMBOL_GPL(nf_conntrack_eventmask_report); - - /* deliver cached events and clear cache entry - must be called with locally - * disabled softirqs */ -+#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS -+void nf_ct_deliver_cached_events(struct nf_conn *ct) -+{ -+ unsigned long events, missed; -+ struct nf_conntrack_ecache *e; -+ struct nf_ct_event item; -+ struct net *net = nf_ct_net(ct); -+ -+ e = nf_ct_ecache_find(ct); -+ if (e == NULL) -+ return; -+ -+ events = xchg(&e->cache, 0); -+ -+ if (!nf_ct_is_confirmed(ct) || nf_ct_is_dying(ct) || !events) -+ return; -+ -+ /* We make a copy of the missed event cache without taking -+ * the lock, thus we may send missed events twice. However, -+ * this does not harm and it happens very rarely. */ -+ missed = e->missed; -+ -+ if (!((events | missed) & e->ctmask)) -+ return; -+ -+ item.ct = ct; -+ item.portid = 0; -+ item.report = 0; -+ -+ atomic_notifier_call_chain(&net->ct.nf_conntrack_chain, -+ events | missed, -+ &item); -+ -+ if (likely(!missed)) -+ return; -+ -+ spin_lock_bh(&ct->lock); -+ e->missed &= ~missed; -+ spin_unlock_bh(&ct->lock); -+} -+#else - void nf_ct_deliver_cached_events(struct nf_conn *ct) - { - struct net *net = nf_ct_net(ct); -@@ -225,6 +302,7 @@ void nf_ct_deliver_cached_events(struct - out_unlock: - rcu_read_unlock(); - } -+#endif - EXPORT_SYMBOL_GPL(nf_ct_deliver_cached_events); - - void nf_ct_expect_event_report(enum ip_conntrack_expect_events event, -@@ -257,6 +335,12 @@ out_unlock: - rcu_read_unlock(); - } - -+#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS -+int nf_conntrack_register_notifier(struct net *net, struct notifier_block *nb) -+{ -+ return atomic_notifier_chain_register(&net->ct.nf_conntrack_chain, nb); -+} -+#else - int nf_conntrack_register_notifier(struct net *net, - struct nf_ct_event_notifier *new) - { -@@ -277,8 +361,15 @@ out_unlock: - mutex_unlock(&nf_ct_ecache_mutex); - return ret; - } -+#endif - EXPORT_SYMBOL_GPL(nf_conntrack_register_notifier); - -+#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS -+int nf_conntrack_unregister_notifier(struct net *net, struct notifier_block *nb) -+{ -+ return atomic_notifier_chain_unregister(&net->ct.nf_conntrack_chain, nb); -+} -+#else - void nf_conntrack_unregister_notifier(struct net *net, - struct nf_ct_event_notifier *new) - { -@@ -292,6 +383,7 @@ void nf_conntrack_unregister_notifier(st - mutex_unlock(&nf_ct_ecache_mutex); - /* synchronize_rcu() is called from ctnetlink_exit. */ - } -+#endif - EXPORT_SYMBOL_GPL(nf_conntrack_unregister_notifier); - - int nf_ct_expect_register_notifier(struct net *net, -diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c -index 04111c1..8c741f7 100644 ---- a/net/netfilter/nf_conntrack_netlink.c -+++ b/net/netfilter/nf_conntrack_netlink.c -@@ -28,6 +28,11 @@ - #include - #include - #include -+ -+#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS -+#include -+#endif -+ - #include - - #include -@@ -618,14 +623,22 @@ static size_t ctnetlink_nlmsg_size(const - ; - } - -+#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS -+static int ctnetlink_conntrack_event(struct notifier_block *this, -+ unsigned long events, void *ptr) -+#else - static int - ctnetlink_conntrack_event(unsigned int events, struct nf_ct_event *item) -+#endif - { - const struct nf_conntrack_zone *zone; - struct net *net; - struct nlmsghdr *nlh; - struct nfgenmsg *nfmsg; - struct nlattr *nest_parms; -+#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS -+ struct nf_ct_event *item = (struct nf_ct_event *)ptr; -+#endif - struct nf_conn *ct = item->ct; - struct sk_buff *skb; - unsigned int type; -@@ -3290,9 +3303,15 @@ static int ctnetlink_stat_exp_cpu(struct - } - - #ifdef CONFIG_NF_CONNTRACK_EVENTS -+#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS -+static struct notifier_block ctnl_notifier = { -+ .notifier_call = ctnetlink_conntrack_event, -+}; -+#else - static struct nf_ct_event_notifier ctnl_notifier = { - .fcn = ctnetlink_conntrack_event, - }; -+#endif - - static struct nf_exp_event_notifier ctnl_notifier_exp = { - .fcn = ctnetlink_expect_event, diff --git a/target/linux/generic/hack-4.14/999-net-patch-linux-kernel-to-support-shortcut-fe.patch b/target/linux/generic/hack-4.14/999-net-patch-linux-kernel-to-support-shortcut-fe.patch deleted file mode 100644 index 9199c67df3..0000000000 --- a/target/linux/generic/hack-4.14/999-net-patch-linux-kernel-to-support-shortcut-fe.patch +++ /dev/null @@ -1,236 +0,0 @@ ---- a/include/linux/skbuff.h 2019-01-16 20:16:08.325745306 +0800 -+++ b/include/linux/skbuff.h 2019-01-16 20:31:47.288028493 +0800 -@@ -783,6 +783,9 @@ struct sk_buff { - __u8 tc_from_ingress:1; - #endif - __u8 gro_skip:1; -+#ifdef CONFIG_SHORTCUT_FE -+ __u8 fast_forwarded:1; -+#endif - - #ifdef CONFIG_NET_SCHED - __u16 tc_index; /* traffic control index */ ---- a/include/linux/if_bridge.h 2019-01-16 20:51:47.871445535 +0800 -+++ b/include/linux/if_bridge.h 2019-01-16 20:52:26.220269649 +0800 -@@ -54,6 +54,8 @@ struct br_ip_list { - #define BR_DEFAULT_AGEING_TIME (300 * HZ) - - extern void brioctl_set(int (*ioctl_hook)(struct net *, unsigned int, void __user *)); -+extern void br_dev_update_stats(struct net_device *dev, -+ struct rtnl_link_stats64 *nlstats); - - typedef int br_should_route_hook_t(struct sk_buff *skb); - extern br_should_route_hook_t __rcu *br_should_route_hook; ---- a/include/linux/timer.h -+++ b/include/linux/timer.h -@@ -20,6 +20,9 @@ struct timer_list { - void (*function)(unsigned long); - unsigned long data; - u32 flags; -+#ifdef CONFIG_SHORTCUT_FE -+ unsigned long cust_data; -+#endif - - #ifdef CONFIG_LOCKDEP - struct lockdep_map lockdep_map; ---- a/net/Kconfig 2019-01-16 20:36:30.266465286 +0800 -+++ b/net/Kconfig 2019-01-16 20:36:41.980609067 +0800 -@@ -463,3 +463,6 @@ config HAVE_CBPF_JIT - # Extended BPF JIT (eBPF) - config HAVE_EBPF_JIT - bool -+ -+config SHORTCUT_FE -+ bool "Enables kernel network stack path for Shortcut Forwarding Engine ---- a/net/core/dev.c 2019-01-16 20:38:37.274933833 +0800 -+++ b/net/core/dev.c 2019-01-16 20:44:07.773594898 +0800 -@@ -3005,8 +3005,17 @@ static int xmit_one(struct sk_buff *skb, struct net_device *dev, - !(skb->imq_flags & IMQ_F_ENQUEUE)) - #else - if (!list_empty(&ptype_all) || !list_empty(&dev->ptype_all)) -+#endif -+#ifdef CONFIG_SHORTCUT_FE -+ /* If this skb has been fast forwarded then we don't want it to -+ * go to any taps (by definition we're trying to bypass them). -+ */ -+ if (!skb->fast_forwarded) { - #endif - dev_queue_xmit_nit(skb, dev); -+#ifdef CONFIG_SHORTCUT_FE -+ } -+#endif - - #ifdef CONFIG_ETHERNET_PACKET_MANGLE - if (!dev->eth_mangle_tx || -@@ -4315,6 +4324,11 @@ void netdev_rx_handler_unregister(struct - } - EXPORT_SYMBOL_GPL(netdev_rx_handler_unregister); - -+#ifdef CONFIG_SHORTCUT_FE -+int (*athrs_fast_nat_recv)(struct sk_buff *skb) __rcu __read_mostly; -+EXPORT_SYMBOL_GPL(athrs_fast_nat_recv); -+#endif -+ - /* - * Limit the use of PFMEMALLOC reserves to those protocols that implement - * the special handling of PFMEMALLOC skbs. -@@ -4362,6 +4376,9 @@ static int __netif_receive_skb_core(stru - bool deliver_exact = false; - int ret = NET_RX_DROP; - __be16 type; -+#ifdef CONFIG_SHORTCUT_FE -+ int (*fast_recv)(struct sk_buff *skb); -+#endif - - net_timestamp_check(!netdev_tstamp_prequeue, skb); - -@@ -4388,6 +4405,16 @@ another_round: - goto out; - } - -+#ifdef CONFIG_SHORTCUT_FE -+ fast_recv = rcu_dereference(athrs_fast_nat_recv); -+ if (fast_recv) { -+ if (fast_recv(skb)) { -+ ret = NET_RX_SUCCESS; -+ goto out; -+ } -+ } -+#endif -+ - if (skb_skip_tc_classify(skb)) - goto skip_classify; - ---- a/net/netfilter/nf_conntrack_proto_tcp.c 2019-01-16 20:47:40.886993297 +0800 -+++ b/net/netfilter/nf_conntrack_proto_tcp.c 2019-01-16 20:48:57.700570104 +0800 -@@ -35,11 +35,17 @@ - - /* Do not check the TCP window for incoming packets */ - static int nf_ct_tcp_no_window_check __read_mostly = 1; -+#ifdef CONFIG_SHORTCUT_FE -+EXPORT_SYMBOL_GPL(nf_ct_tcp_no_window_check); -+#endif - - /* "Be conservative in what you do, - be liberal in what you accept from others." - If it's non-zero, we mark only out of window RST segments as INVALID. */ - static int nf_ct_tcp_be_liberal __read_mostly = 0; -+#ifdef CONFIG_SHORTCUT_FE -+EXPORT_SYMBOL_GPL(nf_ct_tcp_be_liberal); -+#endif - - /* If it is set to zero, we disable picking up already established - connections. */ ---- a/net/bridge/br_if.c 2019-01-16 20:54:51.919367044 +0800 -+++ b/net/bridge/br_if.c 2019-01-16 20:55:53.812401263 +0800 -@@ -653,3 +653,26 @@ void br_port_flags_change(struct net_bri - if (mask & BR_AUTO_MASK) - nbp_update_port_count(br); - } -+ -+/* Update bridge statistics for bridge packets processed by offload engines */ -+void br_dev_update_stats(struct net_device *dev, -+ struct rtnl_link_stats64 *nlstats) -+{ -+ struct net_bridge *br; -+ struct pcpu_sw_netstats *stats; -+ -+ /* Is this a bridge? */ -+ if (!(dev->priv_flags & IFF_EBRIDGE)) -+ return; -+ -+ br = netdev_priv(dev); -+ stats = this_cpu_ptr(br->stats); -+ -+ u64_stats_update_begin(&stats->syncp); -+ stats->rx_packets += nlstats->rx_packets; -+ stats->rx_bytes += nlstats->rx_bytes; -+ stats->tx_packets += nlstats->tx_packets; -+ stats->tx_bytes += nlstats->tx_bytes; -+ u64_stats_update_end(&stats->syncp); -+} -+EXPORT_SYMBOL_GPL(br_dev_update_stats); ---- a/net/netfilter/Kconfig 2019-01-16 21:07:34.543460920 +0800 -+++ b/net/netfilter/Kconfig 2019-01-16 21:08:14.739465937 +0800 -@@ -146,6 +146,14 @@ config NF_CONNTRACK_TIMEOUT - - If unsure, say `N'. - -+config NF_CONNTRACK_CHAIN_EVENTS -+ bool "Register multiple callbacks to ct events" -+ depends on NF_CONNTRACK_EVENTS -+ help -+ Support multiple registrations. -+ -+ If unsure, say `N'. -+ - config NF_CONNTRACK_TIMESTAMP - bool 'Connection tracking timestamping' - depends on NETFILTER_ADVANCED ---- a/net/netfilter/nf_conntrack_ecache.c 2019-01-16 21:12:22.183462975 +0800 -+++ b/net/netfilter/nf_conntrack_ecache.c 2019-01-16 21:26:10.379462031 +0800 -@@ -122,13 +125,17 @@ int nf_conntrack_eventmask_report(unsign - { - int ret = 0; - struct net *net = nf_ct_net(ct); -+#ifndef CONFIG_NF_CONNTRACK_CHAIN_EVENTS - struct nf_ct_event_notifier *notify; -+#endif - struct nf_conntrack_ecache *e; - - rcu_read_lock(); -+#ifndef CONFIG_NF_CONNTRACK_CHAIN_EVENTS - notify = rcu_dereference(net->ct.nf_conntrack_event_cb); - if (!notify) - goto out_unlock; -+#endif - - e = nf_ct_ecache_find(ct); - if (!e) -@@ -146,7 +153,12 @@ int nf_conntrack_eventmask_report(unsign - if (!((eventmask | missed) & e->ctmask)) - goto out_unlock; - -+#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS -+ ret = atomic_notifier_call_chain(&net->ct.nf_conntrack_chain, -+ eventmask | missed, &item); -+#else - ret = notify->fcn(eventmask | missed, &item); -+#endif - if (unlikely(ret < 0 || missed)) { - spin_lock_bh(&ct->lock); - if (ret < 0) { -@@ -179,15 +191,19 @@ void nf_ct_deliver_cached_events(struct - { - struct net *net = nf_ct_net(ct); - unsigned long events, missed; -+#ifndef CONFIG_NF_CONNTRACK_CHAIN_EVENTS - struct nf_ct_event_notifier *notify; -+#endif - struct nf_conntrack_ecache *e; - struct nf_ct_event item; - int ret; - - rcu_read_lock(); -+#ifndef CONFIG_NF_CONNTRACK_CHAIN_EVENTS - notify = rcu_dereference(net->ct.nf_conntrack_event_cb); - if (notify == NULL) - goto out_unlock; -+#endif - - e = nf_ct_ecache_find(ct); - if (e == NULL) -@@ -210,7 +226,13 @@ void nf_ct_deliver_cached_events(struct - item.portid = 0; - item.report = 0; - -+#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS -+ ret = atomic_notifier_call_chain(&net->ct.nf_conntrack_chain, -+ events | missed, -+ &item); -+#else - ret = notify->fcn(events | missed, &item); -+#endif - - if (likely(ret == 0 && !missed)) - goto out_unlock; diff --git a/target/linux/generic/hack-4.9/952-net-conntrack-events-support-multiple-registrant.patch b/target/linux/generic/hack-4.9/952-net-conntrack-events-support-multiple-registrant.patch deleted file mode 100644 index a898c4a695..0000000000 --- a/target/linux/generic/hack-4.9/952-net-conntrack-events-support-multiple-registrant.patch +++ /dev/null @@ -1,345 +0,0 @@ -From 56294a39f14712f868a495f28be6b2e92f0a48c6 Mon Sep 17 00:00:00 2001 -From: Zhi Chen -Date: Tue, 13 Jan 2015 14:28:18 -0800 -Subject: [PATCH 3/3] net: conntrack events, support multiple registrant - -Merging this patch from kernel 3.4: -This was supported by old (.28) kernel versions but removed -because of it's overhead. -But we need this feature for NA connection manager. Both ipv4 -and ipv6 modules needs to register themselves to ct events. - -Change-Id: Iebfb254590fb594f5baf232f849d1b7ae45ef757 -Signed-off-by: Zhi Chen ---- - include/net/netfilter/nf_conntrack_ecache.h | 13 +++- - include/net/netns/conntrack.h | 4 ++ - net/netfilter/Kconfig | 8 +++ - net/netfilter/nf_conntrack_core.c | 4 ++ - net/netfilter/nf_conntrack_ecache.c | 92 +++++++++++++++++++++++++++++ - net/netfilter/nf_conntrack_netlink.c | 19 ++++++ - 6 files changed, 139 insertions(+), 1 deletion(-) - -diff --git a/include/net/netfilter/nf_conntrack_ecache.h b/include/net/netfilter/nf_conntrack_ecache.h -index 12d967b..c2b98b6 100644 ---- a/include/net/netfilter/nf_conntrack_ecache.h -+++ b/include/net/netfilter/nf_conntrack_ecache.h -@@ -70,6 +70,10 @@ struct nf_ct_event { - int report; - }; - -+#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS -+extern int nf_conntrack_register_notifier(struct net *net, struct notifier_block *nb); -+extern int nf_conntrack_unregister_notifier(struct net *net, struct notifier_block *nb); -+#else - struct nf_ct_event_notifier { - int (*fcn)(unsigned int events, struct nf_ct_event *item); - }; -@@ -78,6 +82,7 @@ int nf_conntrack_register_notifier(struct net *net, - struct nf_ct_event_notifier *nb); - void nf_conntrack_unregister_notifier(struct net *net, - struct nf_ct_event_notifier *nb); -+#endif - - void nf_ct_deliver_cached_events(struct nf_conn *ct); - int nf_conntrack_eventmask_report(unsigned int eventmask, struct nf_conn *ct, -@@ -86,11 +91,13 @@ int nf_conntrack_eventmask_report(unsigned int eventmask, struct nf_conn *ct, - static inline void - nf_conntrack_event_cache(enum ip_conntrack_events event, struct nf_conn *ct) - { -- struct net *net = nf_ct_net(ct); - struct nf_conntrack_ecache *e; -+#ifndef CONFIG_NF_CONNTRACK_CHAIN_EVENTS -+ struct net *net = nf_ct_net(ct); - - if (!rcu_access_pointer(net->ct.nf_conntrack_event_cb)) - return; -+#endif - - e = nf_ct_ecache_find(ct); - if (e == NULL) -@@ -103,10 +110,12 @@ static inline int - nf_conntrack_event_report(enum ip_conntrack_events event, struct nf_conn *ct, - u32 portid, int report) - { -+#ifndef CONFIG_NF_CONNTRACK_CHAIN_EVENTS - const struct net *net = nf_ct_net(ct); - - if (!rcu_access_pointer(net->ct.nf_conntrack_event_cb)) - return 0; -+#endif - - return nf_conntrack_eventmask_report(1 << event, ct, portid, report); - } -@@ -114,10 +123,12 @@ nf_conntrack_event_report(enum ip_conntrack_events event, struct nf_conn *ct, - static inline int - nf_conntrack_event(enum ip_conntrack_events event, struct nf_conn *ct) - { -+#ifndef CONFIG_NF_CONNTRACK_CHAIN_EVENTS - const struct net *net = nf_ct_net(ct); - - if (!rcu_access_pointer(net->ct.nf_conntrack_event_cb)) - return 0; -+#endif - - return nf_conntrack_eventmask_report(1 << event, ct, 0, 0); - } -diff --git a/include/net/netns/conntrack.h b/include/net/netns/conntrack.h -index e469e85..1d31db8 100644 ---- a/include/net/netns/conntrack.h -+++ b/include/net/netns/conntrack.h -@@ -86,7 +86,11 @@ struct netns_ct { - - struct ct_pcpu __percpu *pcpu_lists; - struct ip_conntrack_stat __percpu *stat; -+#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS -+ struct atomic_notifier_head nf_conntrack_chain; -+#else - struct nf_ct_event_notifier __rcu *nf_conntrack_event_cb; -+#endif - struct nf_exp_event_notifier __rcu *nf_expect_event_cb; - struct nf_ip_net nf_ct_proto; - #if defined(CONFIG_NF_CONNTRACK_LABELS) -diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig -index 63073be..08d7aab 100644 ---- a/net/netfilter/Kconfig -+++ b/net/netfilter/Kconfig -@@ -136,6 +136,14 @@ config NF_CONNTRACK_TIMEOUT - - If unsure, say `N'. - -+config NF_CONNTRACK_CHAIN_EVENTS -+ bool "Register multiple callbacks to ct events" -+ depends on NF_CONNTRACK_EVENTS -+ help -+ Support multiple registrations. -+ -+ If unsure, say `N'. -+ - config NF_CONNTRACK_TIMESTAMP - bool 'Connection tracking timestamping' - depends on NETFILTER_ADVANCED -diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c -index 6bd1508..9b81c7c 100644 ---- a/net/netfilter/nf_conntrack_core.c -+++ b/net/netfilter/nf_conntrack_core.c -@@ -1998,6 +1998,10 @@ int nf_conntrack_init_net(struct net *net) - ret = nf_conntrack_proto_pernet_init(net); - if (ret < 0) - goto err_proto; -+ -+#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS -+ ATOMIC_INIT_NOTIFIER_HEAD(&net->ct.nf_conntrack_chain); -+#endif - return 0; - - err_proto: -diff --git a/net/netfilter/nf_conntrack_ecache.c b/net/netfilter/nf_conntrack_ecache.c -index da9df2d..e0e2a8f 100644 ---- a/net/netfilter/nf_conntrack_ecache.c -+++ b/net/netfilter/nf_conntrack_ecache.c -@@ -18,6 +18,9 @@ - #include - #include - #include -+#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS -+#include -+#endif - #include - #include - #include -@@ -117,6 +120,38 @@ static void ecache_work(struct work_struct *work) - schedule_delayed_work(&ctnet->ecache_dwork, delay); - } - -+#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS -+int -+nf_conntrack_eventmask_report(unsigned int eventmask, -+ struct nf_conn *ct, -+ u32 portid, -+ int report) -+{ -+ struct nf_conntrack_ecache *e; -+ struct net *net = nf_ct_net(ct); -+ -+ e = nf_ct_ecache_find(ct); -+ if (e == NULL) -+ return 0; -+ -+ if (nf_ct_is_confirmed(ct)) { -+ struct nf_ct_event item = { -+ .ct = ct, -+ .portid = e->portid ? e->portid : portid, -+ .report = report -+ }; -+ /* This is a resent of a destroy event? If so, skip missed */ -+ unsigned long missed = e->portid ? 0 : e->missed; -+ -+ if (!((eventmask | missed) & e->ctmask)) -+ return 0; -+ -+ atomic_notifier_call_chain(&net->ct.nf_conntrack_chain, eventmask | missed, &item); -+ } -+ -+ return 0; -+} -+#else - int nf_conntrack_eventmask_report(unsigned int eventmask, struct nf_conn *ct, - u32 portid, int report) - { -@@ -171,10 +206,52 @@ int nf_conntrack_eventmask_report(unsigned int eventmask, struct nf_conn *ct, - rcu_read_unlock(); - return ret; - } -+#endif - EXPORT_SYMBOL_GPL(nf_conntrack_eventmask_report); - - /* deliver cached events and clear cache entry - must be called with locally - * disabled softirqs */ -+#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS -+void nf_ct_deliver_cached_events(struct nf_conn *ct) -+{ -+ unsigned long events, missed; -+ struct nf_conntrack_ecache *e; -+ struct nf_ct_event item; -+ struct net *net = nf_ct_net(ct); -+ -+ e = nf_ct_ecache_find(ct); -+ if (e == NULL) -+ return; -+ -+ events = xchg(&e->cache, 0); -+ -+ if (!nf_ct_is_confirmed(ct) || nf_ct_is_dying(ct) || !events) -+ return; -+ -+ /* We make a copy of the missed event cache without taking -+ * the lock, thus we may send missed events twice. However, -+ * this does not harm and it happens very rarely. */ -+ missed = e->missed; -+ -+ if (!((events | missed) & e->ctmask)) -+ return; -+ -+ item.ct = ct; -+ item.portid = 0; -+ item.report = 0; -+ -+ atomic_notifier_call_chain(&net->ct.nf_conntrack_chain, -+ events | missed, -+ &item); -+ -+ if (likely(!missed)) -+ return; -+ -+ spin_lock_bh(&ct->lock); -+ e->missed &= ~missed; -+ spin_unlock_bh(&ct->lock); -+} -+#else - void nf_ct_deliver_cached_events(struct nf_conn *ct) - { - struct net *net = nf_ct_net(ct); -@@ -225,6 +302,7 @@ void nf_ct_deliver_cached_events(struct nf_conn *ct) - out_unlock: - rcu_read_unlock(); - } -+#endif - EXPORT_SYMBOL_GPL(nf_ct_deliver_cached_events); - - void nf_ct_expect_event_report(enum ip_conntrack_expect_events event, -@@ -257,6 +335,12 @@ void nf_ct_expect_event_report(enum ip_conntrack_expect_events event, - rcu_read_unlock(); - } - -+#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS -+int nf_conntrack_register_notifier(struct net *net, struct notifier_block *nb) -+{ -+ return atomic_notifier_chain_register(&net->ct.nf_conntrack_chain, nb); -+} -+#else - int nf_conntrack_register_notifier(struct net *net, - struct nf_ct_event_notifier *new) - { -@@ -277,8 +361,15 @@ int nf_conntrack_register_notifier(struct net *net, - mutex_unlock(&nf_ct_ecache_mutex); - return ret; - } -+#endif - EXPORT_SYMBOL_GPL(nf_conntrack_register_notifier); - -+#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS -+int nf_conntrack_unregister_notifier(struct net *net, struct notifier_block *nb) -+{ -+ return atomic_notifier_chain_unregister(&net->ct.nf_conntrack_chain, nb); -+} -+#else - void nf_conntrack_unregister_notifier(struct net *net, - struct nf_ct_event_notifier *new) - { -@@ -291,6 +382,7 @@ void nf_conntrack_unregister_notifier(struct net *net, - RCU_INIT_POINTER(net->ct.nf_conntrack_event_cb, NULL); - mutex_unlock(&nf_ct_ecache_mutex); - } -+#endif - EXPORT_SYMBOL_GPL(nf_conntrack_unregister_notifier); - - int nf_ct_expect_register_notifier(struct net *net, -diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c -index 04111c1..8c741f7 100644 ---- a/net/netfilter/nf_conntrack_netlink.c -+++ b/net/netfilter/nf_conntrack_netlink.c -@@ -28,6 +28,11 @@ - #include - #include - #include -+ -+#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS -+#include -+#endif -+ - #include - - #include -@@ -615,14 +620,22 @@ static size_t ctnetlink_nlmsg_size(const struct nf_conn *ct) - ; - } - -+#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS -+static int ctnetlink_conntrack_event(struct notifier_block *this, -+ unsigned long events, void *ptr) -+#else - static int - ctnetlink_conntrack_event(unsigned int events, struct nf_ct_event *item) -+#endif - { - const struct nf_conntrack_zone *zone; - struct net *net; - struct nlmsghdr *nlh; - struct nfgenmsg *nfmsg; - struct nlattr *nest_parms; -+#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS -+ struct nf_ct_event *item = (struct nf_ct_event *)ptr; -+#endif - struct nf_conn *ct = item->ct; - struct sk_buff *skb; - unsigned int type; -@@ -3260,9 +3273,15 @@ static int ctnetlink_stat_exp_cpu(struct net *net, struct sock *ctnl, - } - - #ifdef CONFIG_NF_CONNTRACK_EVENTS -+#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS -+static struct notifier_block ctnl_notifier = { -+ .notifier_call = ctnetlink_conntrack_event, -+}; -+#else - static struct nf_ct_event_notifier ctnl_notifier = { - .fcn = ctnetlink_conntrack_event, - }; -+#endif - - static struct nf_exp_event_notifier ctnl_notifier_exp = { - .fcn = ctnetlink_expect_event, --- -2.7.4 - diff --git a/target/linux/generic/pending-4.14/601-add-kernel-imq-support.patch b/target/linux/generic/pending-4.14/601-add-kernel-imq-support.patch deleted file mode 100644 index 5e6cdcfee9..0000000000 --- a/target/linux/generic/pending-4.14/601-add-kernel-imq-support.patch +++ /dev/null @@ -1,2044 +0,0 @@ ---- a/drivers/net/Kconfig 2018-04-22 23:17:57.583718674 -0400 -+++ b/drivers/net/Kconfig 2018-04-22 23:20:39.331768961 -0400 -@@ -277,6 +277,125 @@ - depends on RIONET - default "128" - -+config IMQ -+ tristate "IMQ (intermediate queueing device) support" -+ depends on NETDEVICES && NETFILTER -+ ---help--- -+ The IMQ device(s) is used as placeholder for QoS queueing -+ disciplines. Every packet entering/leaving the IP stack can be -+ directed through the IMQ device where it's enqueued/dequeued to the -+ attached qdisc. This allows you to treat network devices as classes -+ and distribute bandwidth among them. Iptables is used to specify -+ through which IMQ device, if any, packets travel. -+ -+ More information at: https://github.com/imq/linuximq -+ -+ To compile this driver as a module, choose M here: the module -+ will be called imq. If unsure, say N. -+ -+choice -+ prompt "IMQ behavior (PRE/POSTROUTING)" -+ depends on IMQ -+ default IMQ_BEHAVIOR_AB -+ help -+ This setting defines how IMQ behaves in respect to its -+ hooking in PREROUTING and POSTROUTING. -+ -+ IMQ can work in any of the following ways: -+ -+ PREROUTING | POSTROUTING -+ -----------------|------------------- -+ #1 After NAT | After NAT -+ #2 After NAT | Before NAT -+ #3 Before NAT | After NAT -+ #4 Before NAT | Before NAT -+ -+ The default behavior is to hook before NAT on PREROUTING -+ and after NAT on POSTROUTING (#3). -+ -+ This settings are specially usefull when trying to use IMQ -+ to shape NATed clients. -+ -+ More information can be found at: https://github.com/imq/linuximq -+ -+ If not sure leave the default settings alone. -+ -+config IMQ_BEHAVIOR_AA -+ bool "IMQ AA" -+ help -+ This setting defines how IMQ behaves in respect to its -+ hooking in PREROUTING and POSTROUTING. -+ -+ Choosing this option will make IMQ hook like this: -+ -+ PREROUTING: After NAT -+ POSTROUTING: After NAT -+ -+ More information can be found at: https://github.com/imq/linuximq -+ -+ If not sure leave the default settings alone. -+ -+config IMQ_BEHAVIOR_AB -+ bool "IMQ AB" -+ help -+ This setting defines how IMQ behaves in respect to its -+ hooking in PREROUTING and POSTROUTING. -+ -+ Choosing this option will make IMQ hook like this: -+ -+ PREROUTING: After NAT -+ POSTROUTING: Before NAT -+ -+ More information can be found at: https://github.com/imq/linuximq -+ -+ If not sure leave the default settings alone. -+ -+config IMQ_BEHAVIOR_BA -+ bool "IMQ BA" -+ help -+ This setting defines how IMQ behaves in respect to its -+ hooking in PREROUTING and POSTROUTING. -+ -+ Choosing this option will make IMQ hook like this: -+ -+ PREROUTING: Before NAT -+ POSTROUTING: After NAT -+ -+ More information can be found at: https://github.com/imq/linuximq -+ -+ If not sure leave the default settings alone. -+ -+config IMQ_BEHAVIOR_BB -+ bool "IMQ BB" -+ help -+ This setting defines how IMQ behaves in respect to its -+ hooking in PREROUTING and POSTROUTING. -+ -+ Choosing this option will make IMQ hook like this: -+ -+ PREROUTING: Before NAT -+ POSTROUTING: Before NAT -+ -+ More information can be found at: https://github.com/imq/linuximq -+ -+ If not sure leave the default settings alone. -+ -+endchoice -+ -+config IMQ_NUM_DEVS -+ int "Number of IMQ devices" -+ range 2 16 -+ depends on IMQ -+ default "16" -+ help -+ This setting defines how many IMQ devices will be created. -+ -+ The default value is 16. -+ -+ More information can be found at: https://github.com/imq/linuximq -+ -+ If not sure leave the default settings alone. -+ - config TUN - tristate "Universal TUN/TAP device driver support" - depends on INET ---- a/drivers/net/Makefile 2018-04-22 23:17:57.583718674 -0400 -+++ b/drivers/net/Makefile 2018-04-22 23:20:39.411768985 -0400 -@@ -13,6 +13,7 @@ - obj-$(CONFIG_EQUALIZER) += eql.o - obj-$(CONFIG_IFB) += ifb.o - obj-$(CONFIG_MACSEC) += macsec.o -+obj-$(CONFIG_IMQ) += imq.o - obj-$(CONFIG_MACVLAN) += macvlan.o - obj-$(CONFIG_MACVTAP) += macvtap.o - obj-$(CONFIG_MII) += mii.o ---- /dev/null 2018-03-30 15:39:24.291451107 -0400 -+++ b/drivers/net/imq.c 2018-05-20 11:10:23.471713282 -0400 -@@ -0,0 +1,1268 @@ -+/* -+ * Pseudo-driver for the intermediate queue device. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ * -+ * Authors: Patrick McHardy, -+ * -+ * The first version was written by Martin Devera, -+ * -+ * See Credits.txt -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) -+#include -+#endif -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+static int imq_nf_queue(struct nf_queue_entry *entry, unsigned queue_num); -+ -+static nf_hookfn imq_nf_hook; -+ -+/*static struct nf_hook_ops imq_ops[] = { -+ { -+ // imq_ingress_ipv4 -+ .hook = imq_nf_hook, -+ .pf = PF_INET, -+ .hooknum = NF_INET_PRE_ROUTING, -+#if defined(CONFIG_IMQ_BEHAVIOR_BA) || defined(CONFIG_IMQ_BEHAVIOR_BB) -+ .priority = NF_IP_PRI_MANGLE + 1, -+#else -+ .priority = NF_IP_PRI_NAT_DST + 1, -+#endif -+ }, -+ { -+ // imq_egress_ipv4 -+ .hook = imq_nf_hook, -+ .pf = PF_INET, -+ .hooknum = NF_INET_POST_ROUTING, -+#if defined(CONFIG_IMQ_BEHAVIOR_AA) || defined(CONFIG_IMQ_BEHAVIOR_BA) -+ .priority = NF_IP_PRI_LAST, -+#else -+ .priority = NF_IP_PRI_NAT_SRC - 1, -+#endif -+ }, -+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) -+ { -+ // imq_ingress_ipv6 -+ .hook = imq_nf_hook, -+ .pf = PF_INET6, -+ .hooknum = NF_INET_PRE_ROUTING, -+#if defined(CONFIG_IMQ_BEHAVIOR_BA) || defined(CONFIG_IMQ_BEHAVIOR_BB) -+ .priority = NF_IP6_PRI_MANGLE + 1, -+#else -+ .priority = NF_IP6_PRI_NAT_DST + 1, -+#endif -+ }, -+ { -+ // imq_egress_ipv6 -+ .hook = imq_nf_hook, -+ .pf = PF_INET6, -+ .hooknum = NF_INET_POST_ROUTING, -+#if defined(CONFIG_IMQ_BEHAVIOR_AA) || defined(CONFIG_IMQ_BEHAVIOR_BA) -+ .priority = NF_IP6_PRI_LAST, -+#else -+ .priority = NF_IP6_PRI_NAT_SRC - 1, -+#endif -+ }, -+#endif -+};*/ -+ -+#if defined(CONFIG_IMQ_NUM_DEVS) -+static int numdevs = CONFIG_IMQ_NUM_DEVS; -+#else -+static int numdevs = IMQ_MAX_DEVS; -+#endif -+ -+static struct net_device *imq_devs_cache[IMQ_MAX_DEVS]; -+ -+#define IMQ_MAX_QUEUES 32 -+static int numqueues = 1; -+static u32 imq_hashrnd; -+static int imq_dev_accurate_stats = 1; -+ -+static inline __be16 pppoe_proto(const struct sk_buff *skb) -+{ -+ return *((__be16 *)(skb_mac_header(skb) + ETH_HLEN + -+ sizeof(struct pppoe_hdr))); -+} -+ -+static u16 imq_hash(struct net_device *dev, struct sk_buff *skb) -+{ -+ unsigned int pull_len; -+ u16 protocol = skb->protocol; -+ u32 addr1, addr2; -+ u32 hash, ihl = 0; -+ union { -+ u16 in16[2]; -+ u32 in32; -+ } ports; -+ u8 ip_proto; -+ -+ pull_len = 0; -+ -+recheck: -+ switch (protocol) { -+ case htons(ETH_P_8021Q): { -+ if (unlikely(skb_pull(skb, VLAN_HLEN) == NULL)) -+ goto other; -+ -+ pull_len += VLAN_HLEN; -+ skb->network_header += VLAN_HLEN; -+ -+ protocol = vlan_eth_hdr(skb)->h_vlan_encapsulated_proto; -+ goto recheck; -+ } -+ -+ case htons(ETH_P_PPP_SES): { -+ if (unlikely(skb_pull(skb, PPPOE_SES_HLEN) == NULL)) -+ goto other; -+ -+ pull_len += PPPOE_SES_HLEN; -+ skb->network_header += PPPOE_SES_HLEN; -+ -+ protocol = pppoe_proto(skb); -+ goto recheck; -+ } -+ -+ case htons(ETH_P_IP): { -+ const struct iphdr *iph = ip_hdr(skb); -+ -+ if (unlikely(!pskb_may_pull(skb, sizeof(struct iphdr)))) -+ goto other; -+ -+ addr1 = iph->daddr; -+ addr2 = iph->saddr; -+ -+ ip_proto = !(ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) ? -+ iph->protocol : 0; -+ ihl = ip_hdrlen(skb); -+ -+ break; -+ } -+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) -+ case htons(ETH_P_IPV6): { -+ const struct ipv6hdr *iph = ipv6_hdr(skb); -+ __be16 fo = 0; -+ -+ if (unlikely(!pskb_may_pull(skb, sizeof(struct ipv6hdr)))) -+ goto other; -+ -+ addr1 = iph->daddr.s6_addr32[3]; -+ addr2 = iph->saddr.s6_addr32[3]; -+ ihl = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &ip_proto, -+ &fo); -+ if (unlikely(ihl < 0)) -+ goto other; -+ -+ break; -+ } -+#endif -+ default: -+other: -+ if (pull_len != 0) { -+ skb_push(skb, pull_len); -+ skb->network_header -= pull_len; -+ } -+ -+ return (u16)(ntohs(protocol) % dev->real_num_tx_queues); -+ } -+ -+ if (addr1 > addr2) -+ swap(addr1, addr2); -+ -+ switch (ip_proto) { -+ case IPPROTO_TCP: -+ case IPPROTO_UDP: -+ case IPPROTO_DCCP: -+ case IPPROTO_ESP: -+ case IPPROTO_AH: -+ case IPPROTO_SCTP: -+ case IPPROTO_UDPLITE: { -+ if (likely(skb_copy_bits(skb, ihl, &ports.in32, 4) >= 0)) { -+ if (ports.in16[0] > ports.in16[1]) -+ swap(ports.in16[0], ports.in16[1]); -+ break; -+ } -+ /* fall-through */ -+ } -+ default: -+ ports.in32 = 0; -+ break; -+ } -+ -+ if (pull_len != 0) { -+ skb_push(skb, pull_len); -+ skb->network_header -= pull_len; -+ } -+ -+ hash = jhash_3words(addr1, addr2, ports.in32, imq_hashrnd ^ ip_proto); -+ -+ return (u16)(((u64)hash * dev->real_num_tx_queues) >> 32); -+} -+ -+static inline bool sk_tx_queue_recorded(struct sock *sk) -+{ -+ return (sk_tx_queue_get(sk) >= 0); -+} -+ -+static struct netdev_queue *imq_select_queue(struct net_device *dev, -+ struct sk_buff *skb) -+{ -+ u16 queue_index = 0; -+ u32 hash; -+ -+ if (likely(dev->real_num_tx_queues == 1)) -+ goto out; -+ -+ /* IMQ can be receiving ingress or engress packets. */ -+ -+ /* Check first for if rx_queue is set */ -+ if (skb_rx_queue_recorded(skb)) { -+ queue_index = skb_get_rx_queue(skb); -+ goto out; -+ } -+ -+ /* Check if socket has tx_queue set */ -+ if (sk_tx_queue_recorded(skb->sk)) { -+ queue_index = sk_tx_queue_get(skb->sk); -+ goto out; -+ } -+ -+ /* Try use socket hash */ -+ if (skb->sk && skb->sk->sk_hash) { -+ hash = skb->sk->sk_hash; -+ queue_index = -+ (u16)(((u64)hash * dev->real_num_tx_queues) >> 32); -+ goto out; -+ } -+ -+ /* Generate hash from packet data */ -+ queue_index = imq_hash(dev, skb); -+ -+out: -+ if (unlikely(queue_index >= dev->real_num_tx_queues)) -+ queue_index = (u16)((u32)queue_index % dev->real_num_tx_queues); -+ -+ skb_set_queue_mapping(skb, queue_index); -+ return netdev_get_tx_queue(dev, queue_index); -+} -+ -+static struct net_device_stats *imq_get_stats(struct net_device *dev) -+{ -+ return &dev->stats; -+} -+ -+/* called for packets kfree'd in qdiscs at places other than enqueue */ -+static void imq_skb_destructor(struct sk_buff *skb) -+{ -+ struct nf_queue_entry *entry = skb->nf_queue_entry; -+ -+ skb->nf_queue_entry = NULL; -+ -+ if (entry) { -+ nf_queue_entry_release_refs(entry); -+ kfree(entry); -+ } -+ -+ skb_restore_cb(skb); /* kfree backup */ -+} -+ -+static void imq_done_check_queue_mapping(struct sk_buff *skb, -+ struct net_device *dev) -+{ -+ unsigned int queue_index; -+ -+ /* Don't let queue_mapping be left too large after exiting IMQ */ -+ if (likely(skb->dev != dev && skb->dev != NULL)) { -+ queue_index = skb_get_queue_mapping(skb); -+ if (unlikely(queue_index >= skb->dev->real_num_tx_queues)) { -+ queue_index = (u16)((u32)queue_index % -+ skb->dev->real_num_tx_queues); -+ skb_set_queue_mapping(skb, queue_index); -+ } -+ } else { -+ /* skb->dev was IMQ device itself or NULL, be on safe side and -+ * just clear queue mapping. -+ */ -+ skb_set_queue_mapping(skb, 0); -+ } -+} -+ -+static netdev_tx_t imq_dev_xmit(struct sk_buff *skb, struct net_device *dev) -+{ -+ struct nf_queue_entry *entry = skb->nf_queue_entry; -+ -+ rcu_read_lock(); -+ -+ skb->nf_queue_entry = NULL; -+ netif_trans_update(dev); -+ -+ dev->stats.tx_bytes += skb->len; -+ dev->stats.tx_packets++; -+ -+ if (unlikely(entry == NULL)) { -+ /* We don't know what is going on here.. packet is queued for -+ * imq device, but (probably) not by us. -+ * -+ * If this packet was not send here by imq_nf_queue(), then -+ * skb_save_cb() was not used and skb_free() should not show: -+ * WARNING: IMQ: kfree_skb: skb->cb_next:.. -+ * and/or -+ * WARNING: IMQ: kfree_skb: skb->nf_queue_entry... -+ * -+ * However if this message is shown, then IMQ is somehow broken -+ * and you should report this to linuximq.net. -+ */ -+ -+ /* imq_dev_xmit is black hole that eats all packets, report that -+ * we eat this packet happily and increase dropped counters. -+ */ -+ -+ dev->stats.tx_dropped++; -+ dev_kfree_skb(skb); -+ -+ rcu_read_unlock(); -+ return NETDEV_TX_OK; -+ } -+ -+ skb_restore_cb(skb); /* restore skb->cb */ -+ -+ skb->imq_flags = 0; -+ skb->destructor = NULL; -+ -+ imq_done_check_queue_mapping(skb, dev); -+ -+ nf_reinject(entry, NF_ACCEPT); -+ -+ rcu_read_unlock(); -+ return NETDEV_TX_OK; -+} -+ -+static struct net_device *get_imq_device_by_index(int index) -+{ -+ struct net_device *dev = NULL; -+ struct net *net; -+ char buf[8]; -+ -+ /* get device by name and cache result */ -+ snprintf(buf, sizeof(buf), "imq%d", index); -+ -+ /* Search device from all namespaces. */ -+ for_each_net(net) { -+ dev = dev_get_by_name(net, buf); -+ if (dev) -+ break; -+ } -+ -+ if (WARN_ON_ONCE(dev == NULL)) { -+ /* IMQ device not found. Exotic config? */ -+ return ERR_PTR(-ENODEV); -+ } -+ -+ imq_devs_cache[index] = dev; -+ dev_put(dev); -+ -+ return dev; -+} -+ -+static struct nf_queue_entry *nf_queue_entry_dup(struct nf_queue_entry *e) -+{ -+ struct nf_queue_entry *entry = kmemdup(e, e->size, GFP_ATOMIC); -+ if (entry) { -+ nf_queue_entry_get_refs(entry); -+ return entry; -+ } -+ return NULL; -+} -+ -+#ifdef CONFIG_BRIDGE_NETFILTER -+/* When called from bridge netfilter, skb->data must point to MAC header -+ * before calling skb_gso_segment(). Else, original MAC header is lost -+ * and segmented skbs will be sent to wrong destination. -+ */ -+static void nf_bridge_adjust_skb_data(struct sk_buff *skb) -+{ -+ if (skb->nf_bridge) -+ __skb_push(skb, skb->network_header - skb->mac_header); -+} -+ -+static void nf_bridge_adjust_segmented_data(struct sk_buff *skb) -+{ -+ if (skb->nf_bridge) -+ __skb_pull(skb, skb->network_header - skb->mac_header); -+} -+#else -+#define nf_bridge_adjust_skb_data(s) do {} while (0) -+#define nf_bridge_adjust_segmented_data(s) do {} while (0) -+#endif -+ -+static void free_entry(struct nf_queue_entry *entry) -+{ -+ nf_queue_entry_release_refs(entry); -+ kfree(entry); -+} -+ -+static int __imq_nf_queue(struct nf_queue_entry *entry, struct net_device *dev); -+ -+static int __imq_nf_queue_gso(struct nf_queue_entry *entry, -+ struct net_device *dev, struct sk_buff *skb) -+{ -+ int ret = -ENOMEM; -+ struct nf_queue_entry *entry_seg; -+ -+ nf_bridge_adjust_segmented_data(skb); -+ -+ if (skb->next == NULL) { /* last packet, no need to copy entry */ -+ struct sk_buff *gso_skb = entry->skb; -+ entry->skb = skb; -+ ret = __imq_nf_queue(entry, dev); -+ if (ret) -+ entry->skb = gso_skb; -+ return ret; -+ } -+ -+ skb->next = NULL; -+ -+ entry_seg = nf_queue_entry_dup(entry); -+ if (entry_seg) { -+ entry_seg->skb = skb; -+ ret = __imq_nf_queue(entry_seg, dev); -+ if (ret) -+ free_entry(entry_seg); -+ } -+ return ret; -+} -+ -+static int imq_nf_queue(struct nf_queue_entry *entry, unsigned queue_num) -+{ -+ struct sk_buff *skb, *segs; -+ struct net_device *dev; -+ unsigned int queued; -+ int index, retval, err; -+ -+ index = entry->skb->imq_flags & IMQ_F_IFMASK; -+ if (unlikely(index > numdevs - 1)) { -+ if (net_ratelimit()) -+ pr_warn("IMQ: invalid device specified, highest is %u\n", -+ numdevs - 1); -+ retval = -EINVAL; -+ goto out_no_dev; -+ } -+ -+ /* check for imq device by index from cache */ -+ dev = imq_devs_cache[index]; -+ if (unlikely(!dev)) { -+ dev = get_imq_device_by_index(index); -+ if (IS_ERR(dev)) { -+ retval = PTR_ERR(dev); -+ goto out_no_dev; -+ } -+ } -+ -+ if (unlikely(!(dev->flags & IFF_UP))) { -+ entry->skb->imq_flags = 0; -+ retval = -ECANCELED; -+ goto out_no_dev; -+ } -+ -+ /* Since 3.10.x, GSO handling moved here as result of upstream commit -+ * a5fedd43d5f6c94c71053a66e4c3d2e35f1731a2 (netfilter: move -+ * skb_gso_segment into nfnetlink_queue module). -+ * -+ * Following code replicates the gso handling from -+ * 'net/netfilter/nfnetlink_queue_core.c':nfqnl_enqueue_packet(). -+ */ -+ -+ skb = entry->skb; -+ -+ switch (entry->state.pf) { -+ case NFPROTO_IPV4: -+ skb->protocol = htons(ETH_P_IP); -+ break; -+ case NFPROTO_IPV6: -+ skb->protocol = htons(ETH_P_IPV6); -+ break; -+ } -+ -+ if (!skb_is_gso(entry->skb)) -+ return __imq_nf_queue(entry, dev); -+ -+ nf_bridge_adjust_skb_data(skb); -+ segs = skb_gso_segment(skb, 0); -+ /* Does not use PTR_ERR to limit the number of error codes that can be -+ * returned by nf_queue. For instance, callers rely on -ECANCELED to -+ * mean 'ignore this hook'. -+ */ -+ err = -ENOBUFS; -+ if (IS_ERR(segs)) -+ goto out_err; -+ queued = 0; -+ err = 0; -+ do { -+ struct sk_buff *nskb = segs->next; -+ if (nskb && nskb->next) -+ nskb->cb_next = NULL; -+ if (err == 0) -+ err = __imq_nf_queue_gso(entry, dev, segs); -+ if (err == 0) -+ queued++; -+ else -+ kfree_skb(segs); -+ segs = nskb; -+ } while (segs); -+ -+ if (queued) { -+ if (err) /* some segments are already queued */ -+ free_entry(entry); -+ kfree_skb(skb); -+ return 0; -+ } -+ -+out_err: -+ nf_bridge_adjust_segmented_data(skb); -+ retval = err; -+out_no_dev: -+ return retval; -+} -+ -+static int __imq_nf_queue(struct nf_queue_entry *entry, struct net_device *dev) -+{ -+ struct sk_buff *skb_orig, *skb, *skb_shared, *skb_popd; -+ struct Qdisc *q; -+ struct sk_buff *to_free = NULL; -+ struct netdev_queue *txq; -+ spinlock_t *root_lock; -+ int users; -+ int retval = -EINVAL; -+ unsigned int orig_queue_index; -+ -+ dev->last_rx = jiffies; -+ -+ skb = entry->skb; -+ skb_orig = NULL; -+ -+ /* skb has owner? => make clone */ -+ if (unlikely(skb->destructor)) { -+ skb_orig = skb; -+ skb = skb_clone(skb, GFP_ATOMIC); -+ if (unlikely(!skb)) { -+ retval = -ENOMEM; -+ goto out; -+ } -+ skb->cb_next = NULL; -+ entry->skb = skb; -+ } -+ -+ dev->stats.rx_bytes += skb->len; -+ dev->stats.rx_packets++; -+ -+ if (!skb->dev) { -+ /* skb->dev == NULL causes problems, try the find cause. */ -+ if (net_ratelimit()) { -+ dev_warn(&dev->dev, -+ "received packet with skb->dev == NULL\n"); -+ dump_stack(); -+ } -+ -+ skb->dev = dev; -+ } -+ -+ /* Disables softirqs for lock below */ -+ rcu_read_lock_bh(); -+ -+ /* Multi-queue selection */ -+ orig_queue_index = skb_get_queue_mapping(skb); -+ txq = imq_select_queue(dev, skb); -+ -+ q = rcu_dereference(txq->qdisc); -+ if (unlikely(!q->enqueue)) -+ goto packet_not_eaten_by_imq_dev; -+ -+ skb->nf_queue_entry = entry; -+ root_lock = qdisc_lock(q); -+ spin_lock(root_lock); -+ -+ users = refcount_read(&skb->users); -+ -+ skb_shared = skb_get(skb); /* increase reference count by one */ -+ -+ /* backup skb->cb, as qdisc layer will overwrite it */ -+ skb_save_cb(skb_shared); -+ qdisc_enqueue_root(skb_shared, q, &to_free); /* might kfree_skb */ -+ if (likely(refcount_read(&skb_shared->users) == users + 1)) { -+ bool validate; -+ -+ kfree_skb(skb_shared); /* decrease reference count by one */ -+ -+ skb->destructor = &imq_skb_destructor; -+ -+ skb_popd = qdisc_dequeue_skb(q, &validate); -+ -+ /* cloned? */ -+ if (unlikely(skb_orig)) -+ kfree_skb(skb_orig); /* free original */ -+ -+ spin_unlock(root_lock); -+ -+#if 0 -+ /* schedule qdisc dequeue */ -+ __netif_schedule(q); -+#else -+ if (likely(skb_popd)) { -+ /* Note that we validate skb (GSO, checksum, ...) outside of locks */ -+ if (validate) -+ skb_popd = validate_xmit_skb_list(skb_popd, dev); -+ -+ if (skb_popd) { -+ int dummy_ret; -+ int cpu = smp_processor_id(); /* ok because BHs are off */ -+ -+ txq = skb_get_tx_queue(dev, skb_popd); -+ /* -+ IMQ device will not be frozen or stoped, and it always be successful. -+ So we need not check its status and return value to accelerate. -+ */ -+ if (imq_dev_accurate_stats && txq->xmit_lock_owner != cpu) { -+ HARD_TX_LOCK(dev, txq, cpu); -+ if (!netif_xmit_frozen_or_stopped(txq)) { -+ dev_hard_start_xmit(skb_popd, dev, txq, &dummy_ret); -+ } -+ HARD_TX_UNLOCK(dev, txq); -+ } else { -+ if (!netif_xmit_frozen_or_stopped(txq)) { -+ dev_hard_start_xmit(skb_popd, dev, txq, &dummy_ret); -+ } -+ } -+ } -+ } else { -+ /* No ready skb, then schedule it */ -+ __netif_schedule(q); -+ } -+#endif -+ rcu_read_unlock_bh(); -+ retval = 0; -+ goto out; -+ } else { -+ skb_restore_cb(skb_shared); /* restore skb->cb */ -+ skb->nf_queue_entry = NULL; -+ /* -+ * qdisc dropped packet and decreased skb reference count of -+ * skb, so we don't really want to and try refree as that would -+ * actually destroy the skb. -+ */ -+ spin_unlock(root_lock); -+ goto packet_not_eaten_by_imq_dev; -+ } -+ -+packet_not_eaten_by_imq_dev: -+ skb_set_queue_mapping(skb, orig_queue_index); -+ rcu_read_unlock_bh(); -+ -+ /* cloned? restore original */ -+ if (unlikely(skb_orig)) { -+ kfree_skb(skb); -+ entry->skb = skb_orig; -+ } -+ retval = -1; -+out: -+ if (unlikely(to_free)) { -+ kfree_skb_list(to_free); -+ } -+ return retval; -+} -+static unsigned int imq_nf_hook(void *priv, -+ struct sk_buff *skb, -+ const struct nf_hook_state *state) -+{ -+ return (skb->imq_flags & IMQ_F_ENQUEUE) ? NF_IMQ_QUEUE : NF_ACCEPT; -+} -+ -+static int imq_close(struct net_device *dev) -+{ -+ netif_stop_queue(dev); -+ return 0; -+} -+ -+static int imq_open(struct net_device *dev) -+{ -+ netif_start_queue(dev); -+ return 0; -+} -+ -+static struct device_type imq_device_type = { -+ .name = "imq", -+}; -+ -+static const struct net_device_ops imq_netdev_ops = { -+ .ndo_open = imq_open, -+ .ndo_stop = imq_close, -+ .ndo_start_xmit = imq_dev_xmit, -+ .ndo_get_stats = imq_get_stats, -+}; -+ -+static void imq_setup(struct net_device *dev) -+{ -+ dev->netdev_ops = &imq_netdev_ops; -+ dev->type = ARPHRD_VOID; -+ dev->mtu = 16000; /* too small? */ -+ dev->tx_queue_len = 11000; /* too big? */ -+ dev->flags = IFF_NOARP; -+ dev->features = NETIF_F_SG | NETIF_F_FRAGLIST | -+ NETIF_F_GSO | NETIF_F_HW_CSUM | -+ NETIF_F_HIGHDMA; -+ dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | -+ IFF_TX_SKB_SHARING); -+} -+ -+static int imq_validate(struct nlattr *tb[], struct nlattr *data[], -+ struct netlink_ext_ack *extack) -+{ -+ int ret = 0; -+ -+ if (tb[IFLA_ADDRESS]) { -+ if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) { -+ ret = -EINVAL; -+ goto end; -+ } -+ if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) { -+ ret = -EADDRNOTAVAIL; -+ goto end; -+ } -+ } -+ return 0; -+end: -+ pr_warn("IMQ: imq_validate failed (%d)\n", ret); -+ return ret; -+} -+ -+static struct rtnl_link_ops imq_link_ops __read_mostly = { -+ .kind = "imq", -+ .priv_size = 0, -+ .setup = imq_setup, -+ .validate = imq_validate, -+}; -+ -+static const struct nf_queue_handler imq_nfqh = { -+ .outfn = imq_nf_queue, -+}; -+ -+static inline char *kernel_strdup(const char *str) -+{ -+ char *tmp; -+ long int s; -+ s=strlen(str) + 1; -+ tmp = kmalloc(s, GFP_ATOMIC); -+ if (tmp != NULL) -+ { -+ memcpy(tmp, str, s); -+ } -+ return tmp; -+} -+ -+/* -+ * line is the line to be parsed -- it is not modified in any way -+ * max_pieces indicates number of pieces to return, if negative this is determined dynamically -+ * include_remainder_at_max indicates whether the last piece, when max pieces are reached, -+ * should be what it would normally be (0) or the entire remainder of the line (1) -+ * if max_pieces < 0 this parameter is ignored -+ * -+ * -+ * returns all non-separator pieces in a line -+ * result is dynamically allocated, MUST be freed after call-- even if -+ * line is empty (you still get a valid char** pointer to to a NULL char*) -+ */ -+char** split_on_separators(char* line, char* separators, int num_separators, int max_pieces, int include_remainder_at_max, unsigned long *num_pieces) -+{ -+ char** split; -+ -+ *num_pieces = 0; -+ if(line != NULL) -+ { -+ int split_index; -+ int non_separator_found; -+ char* dup_line; -+ char* start; -+ -+ if(max_pieces < 0) -+ { -+ /* count number of separator characters in line -- this count + 1 is an upperbound on number of pieces */ -+ int separator_count = 0; -+ int line_index; -+ for(line_index = 0; line[line_index] != '\0'; line_index++) -+ { -+ int sep_index; -+ int found = 0; -+ for(sep_index =0; found == 0 && sep_index < num_separators; sep_index++) -+ { -+ found = separators[sep_index] == line[line_index] ? 1 : 0; -+ } -+ separator_count = separator_count+ found; -+ } -+ max_pieces = separator_count + 1; -+ } -+ split = (char**)kmalloc((1+max_pieces)*sizeof(char*), GFP_ATOMIC); -+ split_index = 0; -+ split[split_index] = NULL; -+ -+ -+ dup_line = kernel_strdup(line); -+ start = dup_line; -+ non_separator_found = 0; -+ while(non_separator_found == 0) -+ { -+ int matches = 0; -+ int sep_index; -+ for(sep_index =0; sep_index < num_separators; sep_index++) -+ { -+ matches = matches == 1 || separators[sep_index] == start[0] ? 1 : 0; -+ } -+ non_separator_found = matches==0 || start[0] == '\0' ? 1 : 0; -+ if(non_separator_found == 0) -+ { -+ start++; -+ } -+ } -+ -+ while(start[0] != '\0' && split_index < max_pieces) -+ { -+ /* find first separator index */ -+ int first_separator_index = 0; -+ int separator_found = 0; -+ while( separator_found == 0 ) -+ { -+ int sep_index; -+ for(sep_index =0; separator_found == 0 && sep_index < num_separators; sep_index++) -+ { -+ separator_found = separators[sep_index] == start[first_separator_index] || start[first_separator_index] == '\0' ? 1 : 0; -+ } -+ if(separator_found == 0) -+ { -+ first_separator_index++; -+ } -+ } -+ -+ /* copy next piece to split array */ -+ if(first_separator_index > 0) -+ { -+ char* next_piece = NULL; -+ if(split_index +1 < max_pieces || include_remainder_at_max <= 0) -+ { -+ next_piece = (char*)kmalloc((first_separator_index+1)*sizeof(char), GFP_ATOMIC); -+ memcpy(next_piece, start, first_separator_index); -+ next_piece[first_separator_index] = '\0'; -+ } -+ else -+ { -+ next_piece = kernel_strdup(start); -+ } -+ split[split_index] = next_piece; -+ split[split_index+1] = NULL; -+ split_index++; -+ } -+ -+ -+ /* find next non-separator index, indicating start of next piece */ -+ start = start+ first_separator_index; -+ non_separator_found = 0; -+ while(non_separator_found == 0) -+ { -+ int matches = 0; -+ int sep_index; -+ for(sep_index =0; sep_index < num_separators; sep_index++) -+ { -+ matches = matches == 1 || separators[sep_index] == start[0] ? 1 : 0; -+ } -+ non_separator_found = matches==0 || start[0] == '\0' ? 1 : 0; -+ if(non_separator_found == 0) -+ { -+ start++; -+ } -+ } -+ } -+ kfree(dup_line); -+ *num_pieces = split_index; -+ } -+ else -+ { -+ split = (char**)kmalloc((1)*sizeof(char*), GFP_ATOMIC); -+ split[0] = NULL; -+ } -+ return split; -+} -+ -+ -+/* returns number freed */ -+int free_null_terminated_string_array(char** strs) -+{ -+ unsigned long str_index = 0; -+ if(strs != NULL) -+ { -+ for(str_index=0; strs[str_index] != NULL; str_index++) -+ { -+ kfree(strs[str_index]); -+ } -+ kfree(strs); -+ } -+ return str_index; -+} -+ -+ -+ -+#if defined(CONFIG_IMQ_BEHAVIOR_BA) || defined(CONFIG_IMQ_BEHAVIOR_BB) -+ #define DEFAULT_PRE_TABLE "mangle" -+#else -+ #define DEFAULT_PRE_TABLE "nat" -+#endif -+ -+#if defined(CONFIG_IMQ_BEHAVIOR_AA) || defined(CONFIG_IMQ_BEHAVIOR_BA) -+ #define DEFAULT_POST_TABLE "nat" -+#else -+ #define DEFAULT_POST_TABLE "mangle" -+#endif -+ -+static char* hook_chains = "PREROUTING,POSTROUTING"; -+static char* hook_tables = DEFAULT_PRE_TABLE","DEFAULT_POST_TABLE; -+ -+static struct nf_hook_ops* hook_list = NULL; -+static int hook_list_length = 0; -+ -+static int __net_init imq_nf_register(struct net *net) -+{ -+ int ret; -+ char separators[4] = {',', ' ', '\t', '\0' }; -+ unsigned long num_chains; -+ unsigned long num_tables; -+ char** chain_list = split_on_separators(hook_chains, separators, 3, -1, 0, &num_chains); -+ char** table_list = split_on_separators(hook_tables, separators, 3, -1, 0, &num_tables); -+ int hook_index = 0; -+ -+ hook_list_length = 0; -+ if(num_chains != num_tables) -+ { -+ printk("ERROR: must have same number of chains and tables\n"); -+ return -1; -+ } -+ -+ /* we multiply by 2 here since we need hooks for both IPv4 and IPv6 */ -+ hook_list = (struct nf_hook_ops*)kmalloc(sizeof(struct nf_hook_ops)*((num_chains*2)+1), GFP_ATOMIC); -+ -+ -+ for(hook_index=0; hook_index < num_chains; hook_index++) -+ { -+ char* chain = chain_list[hook_index]; -+ char* table = table_list[hook_index]; -+ int valid = 0; -+ if(strcmp(chain, "PREROUTING") == 0 || strcmp(chain, "POSTROUTING") == 0 || strcmp(chain, "INPUT") == 0 || strcmp(chain, "FORWARD") == 0 || strcmp(chain, "OUTPUT") == 0) -+ { -+ if( strcmp(table, "mangle") == 0 || -+ (strcmp(table, "nat") == 0 && strcmp(chain, "FORWARD") != 0 && strcmp(chain, "INPUT") != 0 ) || -+ (strcmp(table, "filter") == 0 && strcmp(chain, "PREROUTING") != 0 && strcmp(chain, "POSTROUTING") != 0 ) -+ ) -+ { -+ unsigned int chain_id = NF_INET_PRE_ROUTING; -+ int table_id = NF_IP_PRI_MANGLE; -+ int err = 0; -+ -+ valid = 1; -+ -+ if(strcmp(chain, "PREROUTING") == 0) -+ { -+ chain_id = NF_INET_PRE_ROUTING; -+ } -+ else if (strcmp(chain, "POSTROUTING") == 0) -+ { -+ chain_id = NF_INET_POST_ROUTING; -+ } -+ else if (strcmp(chain, "INPUT") == 0) -+ { -+ chain_id = NF_INET_LOCAL_IN; -+ -+ } -+ else if (strcmp(chain, "FORWARD") == 0) -+ { -+ chain_id = NF_INET_FORWARD; -+ -+ } -+ else if (strcmp(chain, "OUTPUT") == 0) -+ { -+ chain_id = NF_INET_LOCAL_OUT; -+ } -+ -+ if(strcmp(table, "mangle") == 0) -+ { -+ table_id = NF_IP_PRI_MANGLE+1; -+ } -+ else if (strcmp(table, "nat") == 0 && strcmp(chain, "POSTROUTING") == 0) -+ { -+ table_id = NF_IP_PRI_NAT_SRC+1; -+ } -+ else if (strcmp(table, "nat") == 0 && strcmp(chain, "POSTROUTING") != 0) -+ { -+ table_id = NF_IP_PRI_NAT_DST+1; -+ } -+ else if (strcmp(table, "filter") == 0) -+ { -+ table_id = NF_IP_PRI_FILTER+1; -+ } -+ (hook_list[hook_list_length]).hook = imq_nf_hook; -+ (hook_list[hook_list_length]).pf = PF_INET; -+ (hook_list[hook_list_length]).hooknum = chain_id; -+ (hook_list[hook_list_length]).priority = table_id; -+ printk(KERN_INFO "\tIMQv4: Hooking IMQ after %s on %s, pf=%d, hooknum=%d, priority=%d\n", table, chain, (hook_list[hook_list_length]).pf, (hook_list[hook_list_length]).hooknum, (hook_list[hook_list_length]).priority); -+ hook_list_length++; -+ -+ #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) -+ if(strcmp(table, "mangle") == 0) -+ { -+ table_id = NF_IP6_PRI_MANGLE+1; -+ } -+ else if (strcmp(table, "nat") == 0 && strcmp(chain, "POSTROUTING") == 0) -+ { -+ table_id = NF_IP6_PRI_NAT_SRC+1; -+ } -+ else if (strcmp(table, "nat") == 0 && strcmp(chain, "POSTROUTING") != 0) -+ { -+ table_id = NF_IP6_PRI_NAT_DST+1; -+ } -+ else if (strcmp(table, "filter") == 0) -+ { -+ table_id = NF_IP6_PRI_FILTER+1; -+ } -+ -+ (hook_list[hook_list_length]).hook = imq_nf_hook; -+ (hook_list[hook_list_length]).pf = PF_INET6; -+ (hook_list[hook_list_length]).hooknum = chain_id; -+ (hook_list[hook_list_length]).priority = table_id; -+ printk(KERN_INFO "\tIMQv6: Hooking IMQ after %s on %s, pf=%d, hooknum=%d, priority=%d\n", table, chain, (hook_list[hook_list_length]).pf, (hook_list[hook_list_length]).hooknum, (hook_list[hook_list_length]).priority); -+ hook_list_length++; -+ #endif -+ } -+ } -+ if(valid == 0) -+ { -+ printk("ERROR: invalid chain/table at index %d (%s/%s)\n", hook_index, chain, table); -+ } -+ } -+ free_null_terminated_string_array(chain_list); -+ free_null_terminated_string_array(table_list); -+ -+ return nf_register_net_hooks(net, hook_list, -+ hook_list_length); -+}; -+ -+ -+static void __net_exit imq_nf_unregister(struct net *net) -+{ -+ nf_unregister_net_hooks(net, hook_list, -+ hook_list_length); -+}; -+ -+static struct pernet_operations imq_net_ops = { -+ .init = imq_nf_register, -+ .exit = imq_nf_unregister, -+}; -+ -+static int __net_init imq_init_hooks(void) -+{ -+ int ret ; -+ nf_register_queue_imq_handler(&imq_nfqh); -+ -+ ret = register_pernet_subsys(&imq_net_ops); -+ if (ret < 0) -+ nf_unregister_queue_imq_handler(); -+ -+ return ret; -+} -+ -+#ifdef CONFIG_LOCKDEP -+ static struct lock_class_key imq_netdev_addr_lock_key; -+ -+ static void __init imq_dev_set_lockdep_one(struct net_device *dev, -+ struct netdev_queue *txq, void *arg) -+ { -+ /* -+ * the IMQ transmit locks can be taken recursively, -+ * for example with one IMQ rule for input- and one for -+ * output network devices in iptables! -+ * until we find a better solution ignore them. -+ */ -+ lockdep_set_novalidate_class(&txq->_xmit_lock); -+ } -+ -+ static void imq_dev_set_lockdep_class(struct net_device *dev) -+ { -+ lockdep_set_class_and_name(&dev->addr_list_lock, -+ &imq_netdev_addr_lock_key, "_xmit_addr_IMQ"); -+ netdev_for_each_tx_queue(dev, imq_dev_set_lockdep_one, NULL); -+} -+#else -+ static inline void imq_dev_set_lockdep_class(struct net_device *dev) -+ { -+ } -+#endif -+ -+static int __init imq_init_one(int index) -+{ -+ struct net_device *dev; -+ int ret; -+ -+ dev = alloc_netdev_mq(0, "imq%d", NET_NAME_UNKNOWN, imq_setup, numqueues); -+ if (!dev) -+ return -ENOMEM; -+ -+ ret = dev_alloc_name(dev, dev->name); -+ if (ret < 0) -+ goto fail; -+ -+ dev->rtnl_link_ops = &imq_link_ops; -+ SET_NETDEV_DEVTYPE(dev, &imq_device_type); -+ ret = register_netdevice(dev); -+ if (ret < 0) -+ goto fail; -+ -+ imq_dev_set_lockdep_class(dev); -+ -+ return 0; -+fail: -+ free_netdev(dev); -+ return ret; -+} -+ -+static int __init imq_init_devs(void) -+{ -+ int err, i; -+ -+ if (numdevs < 1 || numdevs > IMQ_MAX_DEVS) { -+ pr_err("IMQ: numdevs has to be betweed 1 and %u\n", -+ IMQ_MAX_DEVS); -+ return -EINVAL; -+ } -+ -+ if (numqueues < 1 || numqueues > IMQ_MAX_QUEUES) { -+ pr_err("IMQ: numqueues has to be betweed 1 and %u\n", -+ IMQ_MAX_QUEUES); -+ return -EINVAL; -+ } -+ -+ get_random_bytes(&imq_hashrnd, sizeof(imq_hashrnd)); -+ -+ rtnl_lock(); -+ err = __rtnl_link_register(&imq_link_ops); -+ -+ for (i = 0; i < numdevs && !err; i++) -+ err = imq_init_one(i); -+ -+ if (err) { -+ __rtnl_link_unregister(&imq_link_ops); -+ memset(imq_devs_cache, 0, sizeof(imq_devs_cache)); -+ } -+ rtnl_unlock(); -+ -+ return err; -+} -+ -+static int __init imq_init_module(void) -+{ -+ int err; -+ -+#if defined(CONFIG_IMQ_NUM_DEVS) -+ BUILD_BUG_ON(CONFIG_IMQ_NUM_DEVS > 16); -+ BUILD_BUG_ON(CONFIG_IMQ_NUM_DEVS < 2); -+ BUILD_BUG_ON(CONFIG_IMQ_NUM_DEVS - 1 > IMQ_F_IFMASK); -+#endif -+ -+ err = imq_init_devs(); -+ if (err) { -+ pr_err("IMQ: Error trying imq_init_devs(net)\n"); -+ return err; -+ } -+ -+ err = imq_init_hooks(); -+ if (err) { -+ pr_err(KERN_ERR "IMQ: Error trying imq_init_hooks()\n"); -+ rtnl_link_unregister(&imq_link_ops); -+ memset(imq_devs_cache, 0, sizeof(imq_devs_cache)); -+ return err; -+ } -+ -+ pr_info("IMQ driver loaded successfully. (numdevs = %d, numqueues = %d, imq_dev_accurate_stats = %d)\n", -+ numdevs, numqueues, imq_dev_accurate_stats); -+/* -+#if defined(CONFIG_IMQ_BEHAVIOR_BA) || defined(CONFIG_IMQ_BEHAVIOR_BB) -+ pr_info("\tHooking IMQ before NAT on PREROUTING.\n"); -+#else -+ pr_info("\tHooking IMQ after NAT on PREROUTING.\n"); -+#endif -+#if defined(CONFIG_IMQ_BEHAVIOR_AB) || defined(CONFIG_IMQ_BEHAVIOR_BB) -+ pr_info("\tHooking IMQ before NAT on POSTROUTING.\n"); -+#else -+ pr_info("\tHooking IMQ after NAT on POSTROUTING.\n"); -+#endif -+*/ -+ return 0; -+} -+ -+static void __exit imq_unhook(void) -+{ -+ unregister_pernet_subsys(&imq_net_ops); -+ nf_unregister_queue_imq_handler(); -+ -+ kfree(hook_list); -+ hook_list_length = 0; -+} -+ -+static void __exit imq_cleanup_devs(void) -+{ -+ rtnl_link_unregister(&imq_link_ops); -+ memset(imq_devs_cache, 0, sizeof(imq_devs_cache)); -+} -+ -+static void __exit imq_exit_module(void) -+{ -+ imq_unhook(); -+ imq_cleanup_devs(); -+ pr_info("IMQ driver unloaded successfully.\n"); -+} -+ -+module_init(imq_init_module); -+module_exit(imq_exit_module); -+ -+module_param(numdevs, int, 0); -+module_param(numqueues, int, 0); -+module_param(imq_dev_accurate_stats, int, 0); -+module_param(hook_chains, charp, 0); -+module_param(hook_tables, charp, 0); -+MODULE_PARM_DESC(numdevs, "number of IMQ devices (how many imq* devices will be created)"); -+MODULE_PARM_DESC(numqueues, "number of queues per IMQ device"); -+MODULE_PARM_DESC(imq_dev_accurate_stats, "Notify if need the accurate imq device stats"); -+MODULE_PARM_DESC(hook_chains, "netfilter chains in which to insert hooks to IMQ"); -+MODULE_PARM_DESC(hook_tables, "netfilter tables after which to insert hooks to IMQ"); -+ -+MODULE_AUTHOR("https://github.com/imq/linuximq"); -+MODULE_DESCRIPTION("Pseudo-driver for the intermediate queue device. See https://github.com/imq/linuximq/wiki for more information."); -+MODULE_LICENSE("GPL"); -+MODULE_ALIAS_RTNL_LINK("imq"); ---- /dev/null 2018-03-30 15:39:24.291451107 -0400 -+++ b/include/linux/imq.h 2018-04-22 23:20:39.411768985 -0400 -@@ -0,0 +1,13 @@ -+#ifndef _IMQ_H -+#define _IMQ_H -+ -+/* IFMASK (16 device indexes, 0 to 15) and flag(s) fit in 5 bits */ -+#define IMQ_F_BITS 5 -+ -+#define IMQ_F_IFMASK 0x0f -+#define IMQ_F_ENQUEUE 0x10 -+ -+#define IMQ_MAX_DEVS (IMQ_F_IFMASK + 1) -+ -+#endif /* _IMQ_H */ -+ ---- a/include/linux/netdevice.h 2018-04-22 23:18:14.351723881 -0400 -+++ b/include/linux/netdevice.h 2018-04-22 23:20:39.411768985 -0400 -@@ -1785,6 +1785,11 @@ - /* - * Cache lines mostly used on receive path (including eth_type_trans()) - */ -+ -+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) -+ unsigned long last_rx; -+#endif -+ - /* Interface address info used in eth_type_trans() */ - unsigned char *dev_addr; - -@@ -3646,6 +3651,19 @@ - } \ - } - -+#define HARD_TX_LOCK_BH(dev, txq) { \ -+ if ((dev->features & NETIF_F_LLTX) == 0) { \ -+ __netif_tx_lock_bh(txq); \ -+ } \ -+} -+ -+#define HARD_TX_UNLOCK_BH(dev, txq) { \ -+ if ((dev->features & NETIF_F_LLTX) == 0) { \ -+ __netif_tx_unlock_bh(txq); \ -+ } \ -+} -+ -+ - static inline void netif_tx_disable(struct net_device *dev) - { - unsigned int i; ---- /dev/null 2018-03-30 15:39:24.291451107 -0400 -+++ b/include/linux/netfilter/xt_IMQ.h 2018-04-22 23:20:39.415768987 -0400 -@@ -0,0 +1,9 @@ -+#ifndef _XT_IMQ_H -+#define _XT_IMQ_H -+ -+struct xt_imq_info { -+ unsigned int todev; /* target imq device */ -+}; -+ -+#endif /* _XT_IMQ_H */ -+ ---- /dev/null 2018-03-30 15:39:24.291451107 -0400 -+++ b/include/linux/netfilter_ipv4/ipt_IMQ.h 2018-04-22 23:20:39.415768987 -0400 -@@ -0,0 +1,10 @@ -+#ifndef _IPT_IMQ_H -+#define _IPT_IMQ_H -+ -+/* Backwards compatibility for old userspace */ -+#include -+ -+#define ipt_imq_info xt_imq_info -+ -+#endif /* _IPT_IMQ_H */ -+ ---- /dev/null 2018-03-30 15:39:24.291451107 -0400 -+++ b/include/linux/netfilter_ipv6/ip6t_IMQ.h 2018-04-22 23:20:39.415768987 -0400 -@@ -0,0 +1,10 @@ -+#ifndef _IP6T_IMQ_H -+#define _IP6T_IMQ_H -+ -+/* Backwards compatibility for old userspace */ -+#include -+ -+#define ip6t_imq_info xt_imq_info -+ -+#endif /* _IP6T_IMQ_H */ -+ ---- a/include/linux/skbuff.h 2018-04-22 23:18:15.079724108 -0400 -+++ b/include/linux/skbuff.h 2018-04-22 23:20:39.415768987 -0400 -@@ -40,6 +40,10 @@ - #include - #include - #include -+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) -+#include -+#endif -+ - - /* The interface for checksum offload between the stack and networking drivers - * is as follows... -@@ -582,7 +586,7 @@ - typedef unsigned char *sk_buff_data_t; - #endif - --/** -+/** - * struct sk_buff - socket buffer - * @next: Next buffer in list - * @prev: Previous buffer in list -@@ -685,6 +689,9 @@ - * first. This is owned by whoever has the skb queued ATM. - */ - char cb[48] __aligned(8); -+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) -+ void *cb_next; -+#endif - - unsigned long _skb_refdst; - void (*destructor)(struct sk_buff *skb); -@@ -694,6 +701,9 @@ - #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) - unsigned long _nfct; - #endif -+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) -+ struct nf_queue_entry *nf_queue_entry; -+#endif - #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) - struct nf_bridge_info *nf_bridge; - #endif -@@ -771,6 +781,9 @@ - #ifdef CONFIG_NET_SWITCHDEV - __u8 offload_fwd_mark:1; - #endif -+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) -+ __u8 imq_flags:IMQ_F_BITS; -+#endif - #ifdef CONFIG_NET_CLS_ACT - __u8 tc_skip_classify:1; - __u8 tc_at_ingress:1; -@@ -870,7 +883,7 @@ - */ - static inline struct dst_entry *skb_dst(const struct sk_buff *skb) - { -- /* If refdst was not refcounted, check we still are in a -+ /* If refdst was not refcounted, check we still are in a - * rcu_read_lock section - */ - WARN_ON((skb->_skb_refdst & SKB_DST_NOREF) && -@@ -960,6 +973,12 @@ - void consume_skb(struct sk_buff *skb); - void __consume_stateless_skb(struct sk_buff *skb); - void __kfree_skb(struct sk_buff *skb); -+ -+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) -+int skb_save_cb(struct sk_buff *skb); -+int skb_restore_cb(struct sk_buff *skb); -+#endif -+ - extern struct kmem_cache *skbuff_head_cache; - - void kfree_skb_partial(struct sk_buff *skb, bool head_stolen); -@@ -3779,8 +3798,12 @@ - dst->_nfct = src->_nfct; - nf_conntrack_get(skb_nfct(src)); - #endif -+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) -+ dst->imq_flags = src->imq_flags; -+ dst->nf_queue_entry = src->nf_queue_entry; -+#endif - #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) -- dst->nf_bridge = src->nf_bridge; -+ dst->nf_bridge = src->nf_bridge; - nf_bridge_get(src->nf_bridge); - #endif - #if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE) || defined(CONFIG_NF_TABLES) ---- a/include/net/netfilter/nf_queue.h 2018-04-22 23:18:15.199724146 -0400 -+++ b/include/net/netfilter/nf_queue.h 2018-04-22 23:20:39.419768987 -0400 -@@ -31,6 +31,12 @@ - void nf_register_queue_handler(struct net *net, const struct nf_queue_handler *qh); - void nf_unregister_queue_handler(struct net *net); - void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict); -+void nf_queue_entry_release_refs(struct nf_queue_entry *entry); -+ -+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) -+void nf_register_queue_imq_handler(const struct nf_queue_handler *qh); -+void nf_unregister_queue_imq_handler(void); -+#endif - - void nf_queue_entry_get_refs(struct nf_queue_entry *entry); - void nf_queue_entry_release_refs(struct nf_queue_entry *entry); ---- a/include/net/pkt_sched.h 2018-04-22 23:18:15.219724151 -0400 -+++ b/include/net/pkt_sched.h 2018-04-22 23:20:39.419768987 -0400 -@@ -109,6 +109,8 @@ - - void __qdisc_run(struct Qdisc *q); - -+struct sk_buff *qdisc_dequeue_skb(struct Qdisc *q, bool *validate); -+ - static inline void qdisc_run(struct Qdisc *q) - { - if (qdisc_run_begin(q)) ---- a/include/net/sch_generic.h 2018-04-22 23:18:15.227724154 -0400 -+++ b/include/net/sch_generic.h 2018-04-22 23:20:39.419768987 -0400 -@@ -567,6 +567,13 @@ - return sch->enqueue(skb, sch, to_free); - } - -+static inline int qdisc_enqueue_root(struct sk_buff *skb, struct Qdisc *sch, -+ struct sk_buff **to_free) -+{ -+ qdisc_skb_cb(skb)->pkt_len = skb->len; -+ return qdisc_enqueue(skb, sch, to_free) & NET_XMIT_MASK; -+} -+ - static inline bool qdisc_is_percpu_stats(const struct Qdisc *q) - { - return q->flags & TCQ_F_CPUSTATS; ---- a/include/uapi/linux/netfilter.h 2018-04-22 23:18:16.159724442 -0400 -+++ b/include/uapi/linux/netfilter.h 2018-04-22 23:20:39.419768987 -0400 -@@ -14,7 +14,8 @@ - #define NF_QUEUE 3 - #define NF_REPEAT 4 - #define NF_STOP 5 /* Deprecated, for userspace nf_queue compatibility. */ --#define NF_MAX_VERDICT NF_STOP -+#define NF_IMQ_QUEUE 6 -+#define NF_MAX_VERDICT NF_IMQ_QUEUE - - /* we overload the higher bits for encoding auxiliary data such as the queue - * number or errno values. Not nice, but better than additional function ---- a/net/core/dev.c 2018-04-22 23:18:24.391727000 -0400 -+++ b/net/core/dev.c 2018-04-22 23:20:39.419768987 -0400 -@@ -143,6 +143,9 @@ - #include - #include - #include -+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) -+#include -+#endif - #include - #include - -@@ -2979,7 +2982,12 @@ - unsigned int len; - int rc; - -+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) -+ if ((!list_empty(&ptype_all) || !list_empty(&dev->ptype_all)) && -+ !(skb->imq_flags & IMQ_F_ENQUEUE)) -+#else - if (!list_empty(&ptype_all) || !list_empty(&dev->ptype_all)) -+#endif - dev_queue_xmit_nit(skb, dev); - - #ifdef CONFIG_ETHERNET_PACKET_MANGLE -@@ -3028,6 +3036,8 @@ - return skb; - } - -+EXPORT_SYMBOL_GPL(dev_hard_start_xmit); -+ - static struct sk_buff *validate_xmit_vlan(struct sk_buff *skb, - netdev_features_t features) - { ---- a/net/core/skbuff.c 2018-04-22 23:18:24.395727003 -0400 -+++ b/net/core/skbuff.c 2018-04-22 23:20:39.419768987 -0400 -@@ -82,6 +82,87 @@ - static struct kmem_cache *skbuff_fclone_cache __read_mostly; - int sysctl_max_skb_frags __read_mostly = MAX_SKB_FRAGS; - EXPORT_SYMBOL(sysctl_max_skb_frags); -+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) -+static struct kmem_cache *skbuff_cb_store_cache __read_mostly; -+#endif -+ -+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) -+/* Control buffer save/restore for IMQ devices */ -+struct skb_cb_table { -+ char cb[48] __aligned(8); -+ void *cb_next; -+ atomic_t refcnt; -+}; -+ -+static DEFINE_SPINLOCK(skb_cb_store_lock); -+ -+int skb_save_cb(struct sk_buff *skb) -+{ -+ struct skb_cb_table *next; -+ -+ next = kmem_cache_alloc(skbuff_cb_store_cache, GFP_ATOMIC); -+ if (!next) -+ return -ENOMEM; -+ -+ BUILD_BUG_ON(sizeof(skb->cb) != sizeof(next->cb)); -+ -+ memcpy(next->cb, skb->cb, sizeof(skb->cb)); -+ next->cb_next = skb->cb_next; -+ -+ atomic_set(&next->refcnt, 1); -+ -+ skb->cb_next = next; -+ return 0; -+} -+EXPORT_SYMBOL(skb_save_cb); -+ -+int skb_restore_cb(struct sk_buff *skb) -+{ -+ struct skb_cb_table *next; -+ -+ if (!skb->cb_next) -+ return 0; -+ -+ next = skb->cb_next; -+ -+ BUILD_BUG_ON(sizeof(skb->cb) != sizeof(next->cb)); -+ -+ memcpy(skb->cb, next->cb, sizeof(skb->cb)); -+ skb->cb_next = next->cb_next; -+ -+ spin_lock(&skb_cb_store_lock); -+ -+ if (atomic_dec_and_test(&next->refcnt)) -+ kmem_cache_free(skbuff_cb_store_cache, next); -+ -+ spin_unlock(&skb_cb_store_lock); -+ -+ return 0; -+} -+EXPORT_SYMBOL(skb_restore_cb); -+ -+static void skb_copy_stored_cb(struct sk_buff * , const struct sk_buff * ) __attribute__ ((unused)); -+static void skb_copy_stored_cb(struct sk_buff *new, const struct sk_buff *__old) -+{ -+ struct skb_cb_table *next; -+ struct sk_buff *old; -+ -+ if (!__old->cb_next) { -+ new->cb_next = NULL; -+ return; -+ } -+ -+ spin_lock(&skb_cb_store_lock); -+ -+ old = (struct sk_buff *)__old; -+ -+ next = old->cb_next; -+ atomic_inc(&next->refcnt); -+ new->cb_next = next; -+ -+ spin_unlock(&skb_cb_store_lock); -+} -+#endif - - /** - * skb_panic - private function for out-of-line support -@@ -628,6 +709,28 @@ - WARN_ON(in_irq()); - skb->destructor(skb); - } -+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) -+ /* -+ * This should not happen. When it does, avoid memleak by restoring -+ * the chain of cb-backups. -+ */ -+ while (skb->cb_next != NULL) { -+ if (net_ratelimit()) -+ pr_warn("IMQ: kfree_skb: skb->cb_next: %08x\n", -+ (unsigned int)(uintptr_t)skb->cb_next); -+ -+ skb_restore_cb(skb); -+ } -+ /* -+ * This should not happen either, nf_queue_entry is nullified in -+ * imq_dev_xmit(). If we have non-NULL nf_queue_entry then we are -+ * leaking entry pointers, maybe memory. We don't know if this is -+ * pointer to already freed memory, or should this be freed. -+ * If this happens we need to add refcounting, etc for nf_queue_entry. -+ */ -+ if (skb->nf_queue_entry && net_ratelimit()) -+ pr_warn("%s\n", "IMQ: kfree_skb: skb->nf_queue_entry != NULL"); -+#endif - #if IS_ENABLED(CONFIG_NF_CONNTRACK) - nf_conntrack_put(skb_nfct(skb)); - #endif -@@ -817,6 +920,10 @@ - new->sp = secpath_get(old->sp); - #endif - __nf_copy(new, old, false); -+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) -+ new->cb_next = NULL; -+ /*skb_copy_stored_cb(new, old);*/ -+#endif - - /* Note : this field could be in headers_start/headers_end section - * It is not yet because we do not want to have a 16 bit hole -@@ -3915,6 +4022,13 @@ - 0, - SLAB_HWCACHE_ALIGN|SLAB_PANIC, - NULL); -+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) -+ skbuff_cb_store_cache = kmem_cache_create("skbuff_cb_store_cache", -+ sizeof(struct skb_cb_table), -+ 0, -+ SLAB_HWCACHE_ALIGN|SLAB_PANIC, -+ NULL); -+#endif - } - - static int ---- a/net/netfilter/core.c 2018-04-22 23:18:26.019727507 -0400 -+++ b/net/netfilter/core.c 2018-04-22 23:20:39.419768987 -0400 -@@ -457,6 +457,11 @@ - if (ret == 0) - ret = -EPERM; - return ret; -+ case NF_IMQ_QUEUE: -+ ret = nf_queue(skb, state, e, s, verdict); -+ if (ret == -ECANCELED) -+ continue; -+ return ret; - case NF_QUEUE: - ret = nf_queue(skb, state, e, s, verdict); - if (ret == 1) ---- a/net/netfilter/Kconfig 2018-04-22 23:18:25.751727424 -0400 -+++ b/net/netfilter/Kconfig 2018-04-22 23:20:39.419768987 -0400 -@@ -902,6 +902,18 @@ - - To compile it as a module, choose M here. If unsure, say N. - -+config NETFILTER_XT_TARGET_IMQ -+ tristate '"IMQ" target support' -+ depends on NETFILTER_XTABLES -+ depends on IP_NF_MANGLE || IP6_NF_MANGLE -+ select IMQ -+ default m if NETFILTER_ADVANCED=n -+ help -+ This option adds a `IMQ' target which is used to specify if and -+ to which imq device packets should get enqueued/dequeued. -+ -+ To compile it as a module, choose M here. If unsure, say N. -+ - config NETFILTER_XT_TARGET_MARK - tristate '"MARK" target support' - depends on NETFILTER_ADVANCED ---- a/net/netfilter/Makefile 2018-04-22 23:18:25.711727410 -0400 -+++ b/net/netfilter/Makefile 2018-04-22 23:20:39.423768990 -0400 -@@ -136,6 +136,7 @@ - obj-$(CONFIG_NETFILTER_XT_TARGET_FLOWOFFLOAD) += xt_FLOWOFFLOAD.o - obj-$(CONFIG_NETFILTER_XT_TARGET_HL) += xt_HL.o - obj-$(CONFIG_NETFILTER_XT_TARGET_HMARK) += xt_HMARK.o -+obj-$(CONFIG_NETFILTER_XT_TARGET_IMQ) += xt_IMQ.o - obj-$(CONFIG_NETFILTER_XT_TARGET_LED) += xt_LED.o - obj-$(CONFIG_NETFILTER_XT_TARGET_LOG) += xt_LOG.o - obj-$(CONFIG_NETFILTER_XT_TARGET_NETMAP) += xt_NETMAP.o ---- a/net/netfilter/nf_queue.c 2018-04-22 23:18:25.699727407 -0400 -+++ b/net/netfilter/nf_queue.c 2018-04-22 23:22:59.395812579 -0400 -@@ -1,4 +1,4 @@ --/* -+ /* - * Rusty Russell (C)2000 -- This code is GPL. - * Patrick McHardy (c) 2006-2012 - */ -@@ -29,6 +29,23 @@ - * receives, no matter what. - */ - -+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) -+static const struct nf_queue_handler __rcu *queue_imq_handler __read_mostly; -+ -+void nf_register_queue_imq_handler(const struct nf_queue_handler *qh) -+{ -+ rcu_assign_pointer(queue_imq_handler, qh); -+} -+EXPORT_SYMBOL_GPL(nf_register_queue_imq_handler); -+ -+void nf_unregister_queue_imq_handler(void) -+{ -+ RCU_INIT_POINTER(queue_imq_handler, NULL); -+ synchronize_rcu(); -+} -+EXPORT_SYMBOL_GPL(nf_unregister_queue_imq_handler); -+#endif -+ - /* return EBUSY when somebody else is registered, return EEXIST if the - * same handler is registered, return 0 in case of success. */ - void nf_register_queue_handler(struct net *net, const struct nf_queue_handler *qh) -@@ -144,16 +161,29 @@ - - static int __nf_queue(struct sk_buff *skb, const struct nf_hook_state *state, - const struct nf_hook_entries *entries, -- unsigned int index, unsigned int queuenum) -+ unsigned int index, unsigned int verdict) - { - int status = -ENOENT; - struct nf_queue_entry *entry = NULL; - const struct nf_queue_handler *qh; - struct net *net = state->net; - unsigned int route_key_size; -+ unsigned int queuetype = verdict & NF_VERDICT_MASK; -+ unsigned int queuenum = verdict >> NF_VERDICT_QBITS; - - /* QUEUE == DROP if no one is waiting, to be safe. */ -- qh = rcu_dereference(net->nf.queue_handler); -+ -+ if (queuetype == NF_IMQ_QUEUE) { -+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) -+ qh = rcu_dereference(queue_imq_handler); -+#else -+ BUG(); -+ goto err; -+#endif -+ } else { -+ qh = rcu_dereference(net->nf.queue_handler); -+ } -+ - if (!qh) { - status = -ESRCH; - goto err; -@@ -217,8 +248,16 @@ - { - int ret; - -- ret = __nf_queue(skb, state, entries, index, verdict >> NF_VERDICT_QBITS); -+ ret = __nf_queue(skb, state, entries, index, verdict); - if (ret < 0) { -+ -+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) -+ /* IMQ Bypass */ -+ if (ret == -ECANCELED && skb->imq_flags == 0) { -+ return 1; -+ } -+#endif -+ - if (ret == -ESRCH && - (verdict & NF_VERDICT_FLAG_QUEUE_BYPASS)) - return 1; -@@ -302,6 +341,7 @@ - local_bh_enable(); - break; - case NF_QUEUE: -+ case NF_IMQ_QUEUE: - err = nf_queue(skb, &entry->state, hooks, i, verdict); - if (err == 1) - goto next_hook; ---- /dev/null 2018-03-30 15:39:24.291451107 -0400 -+++ b/net/netfilter/xt_IMQ.c 2018-04-22 23:20:39.423768990 -0400 -@@ -0,0 +1,72 @@ -+/* -+ * This target marks packets to be enqueued to an imq device -+ */ -+#include -+#include -+#include -+#include -+#include -+ -+static unsigned int imq_target(struct sk_buff *pskb, -+ const struct xt_action_param *par) -+{ -+ const struct xt_imq_info *mr = par->targinfo; -+ -+ pskb->imq_flags = (mr->todev & IMQ_F_IFMASK) | IMQ_F_ENQUEUE; -+ -+ return XT_CONTINUE; -+} -+ -+static int imq_checkentry(const struct xt_tgchk_param *par) -+{ -+ struct xt_imq_info *mr = par->targinfo; -+ -+ if (mr->todev > IMQ_MAX_DEVS - 1) { -+ pr_warn("IMQ: invalid device specified, highest is %u\n", -+ IMQ_MAX_DEVS - 1); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static struct xt_target xt_imq_reg[] __read_mostly = { -+ { -+ .name = "IMQ", -+ .family = AF_INET, -+ .checkentry = imq_checkentry, -+ .target = imq_target, -+ .targetsize = sizeof(struct xt_imq_info), -+ .table = "mangle", -+ .me = THIS_MODULE -+ }, -+ { -+ .name = "IMQ", -+ .family = AF_INET6, -+ .checkentry = imq_checkentry, -+ .target = imq_target, -+ .targetsize = sizeof(struct xt_imq_info), -+ .table = "mangle", -+ .me = THIS_MODULE -+ }, -+}; -+ -+static int __init imq_init(void) -+{ -+ return xt_register_targets(xt_imq_reg, ARRAY_SIZE(xt_imq_reg)); -+} -+ -+static void __exit imq_fini(void) -+{ -+ xt_unregister_targets(xt_imq_reg, ARRAY_SIZE(xt_imq_reg)); -+} -+ -+module_init(imq_init); -+module_exit(imq_fini); -+ -+MODULE_AUTHOR("https://github.com/imq/linuximq"); -+MODULE_DESCRIPTION("Pseudo-driver for the intermediate queue device. See https://github.com/imq/linuximq/wiki for more information."); -+MODULE_LICENSE("GPL"); -+MODULE_ALIAS("ipt_IMQ"); -+MODULE_ALIAS("ip6t_IMQ"); -+ ---- a/net/sched/sch_generic.c 2018-04-22 23:18:26.623727693 -0400 -+++ b/net/sched/sch_generic.c 2018-04-22 23:20:39.423768990 -0400 -@@ -158,6 +158,14 @@ - return skb; - } - -+struct sk_buff *qdisc_dequeue_skb(struct Qdisc *q, bool *validate) -+{ -+ int packets; -+ -+ return dequeue_skb(q, validate, &packets); -+} -+EXPORT_SYMBOL(qdisc_dequeue_skb); -+ - /* - * Transmit possibly several skbs, and handle the return status as - * required. Owning running seqcount bit guarantees that diff --git a/target/linux/generic/pending-4.14/608-add-kernel-gargoyle-netfilter-match-modules.patch b/target/linux/generic/pending-4.14/608-add-kernel-gargoyle-netfilter-match-modules.patch deleted file mode 100644 index 7fb6078ac8..0000000000 --- a/target/linux/generic/pending-4.14/608-add-kernel-gargoyle-netfilter-match-modules.patch +++ /dev/null @@ -1,10864 +0,0 @@ ---- /dev/null -+++ b/include/linux/netfilter_ipv4/ipt_bandwidth.h -@@ -0,0 +1,106 @@ -+/* bandwidth -- An iptables extension for bandwidth monitoring/control -+ * Can be used to efficiently monitor bandwidth and/or implement bandwidth quotas -+ * Can be queried using the iptbwctl userspace library -+ * Originally designed for use with Gargoyle router firmware (gargoyle-router.com) -+ * -+ * -+ * Copyright © 2009 by Eric Bishop -+ * -+ * This file is free software: you may copy, redistribute and/or modify it -+ * under the terms of the GNU General Public License as published by the -+ * Free Software Foundation, either version 2 of the License, or (at your -+ * option) any later version. -+ * -+ * This file is distributed in the hope that it will be useful, but -+ * WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+ -+#ifndef _IPT_BANDWIDTH_H -+#define _IPT_BANDWIDTH_H -+ -+/*flags -- first three don't map to parameters the rest do */ -+#define BANDWIDTH_INITIALIZED 1 -+#define BANDWIDTH_REQUIRES_SUBNET 2 -+#define BANDWIDTH_SUBNET 4 -+#define BANDWIDTH_CMP 8 -+#define BANDWIDTH_CURRENT 16 -+#define BANDWIDTH_RESET_INTERVAL 32 -+#define BANDWIDTH_RESET_TIME 64 -+#define BANDWIDTH_LAST_BACKUP 128 -+ -+ -+/* parameter defs that don't map to flag bits */ -+#define BANDWIDTH_TYPE 70 -+#define BANDWIDTH_ID 71 -+#define BANDWIDTH_GT 72 -+#define BANDWIDTH_LT 73 -+#define BANDWIDTH_MONITOR 74 -+#define BANDWIDTH_CHECK 75 -+#define BANDWIDTH_CHECK_NOSWAP 76 -+#define BANDWIDTH_CHECK_SWAP 77 -+#define BANDWIDTH_NUM_INTERVALS 78 -+ -+/* possible reset intervals */ -+#define BANDWIDTH_MINUTE 80 -+#define BANDWIDTH_HOUR 81 -+#define BANDWIDTH_DAY 82 -+#define BANDWIDTH_WEEK 83 -+#define BANDWIDTH_MONTH 84 -+#define BANDWIDTH_NEVER 85 -+ -+/* possible monitoring types */ -+#define BANDWIDTH_COMBINED 90 -+#define BANDWIDTH_INDIVIDUAL_SRC 91 -+#define BANDWIDTH_INDIVIDUAL_DST 92 -+#define BANDWIDTH_INDIVIDUAL_LOCAL 93 -+#define BANDWIDTH_INDIVIDUAL_REMOTE 94 -+ -+ -+ -+/* socket id parameters (for userspace i/o) */ -+#define BANDWIDTH_SET 2048 -+#define BANDWIDTH_GET 2049 -+ -+/* max id length */ -+#define BANDWIDTH_MAX_ID_LENGTH 50 -+ -+/* 4 bytes for total number of entries, 100 entries of 12 bytes each, + 1 byte indicating whether all have been dumped */ -+#define BANDWIDTH_QUERY_LENGTH 1205 -+#define BANDWIDTH_ENTRY_LENGTH 12 -+ -+ -+struct ipt_bandwidth_info -+{ -+ char id[BANDWIDTH_MAX_ID_LENGTH]; -+ unsigned char type; -+ unsigned char check_type; -+ uint32_t local_subnet; -+ uint32_t local_subnet_mask; -+ -+ unsigned char cmp; -+ unsigned char reset_is_constant_interval; -+ time_t reset_interval; //specific fixed type (see above) or interval length in seconds -+ time_t reset_time; //seconds from start of month/week/day/hour/minute to do reset, or start point of interval if it is a constant interval -+ uint64_t bandwidth_cutoff; -+ uint64_t current_bandwidth; -+ time_t next_reset; -+ time_t previous_reset; -+ time_t last_backup_time; -+ -+ uint32_t num_intervals_to_save; -+ -+ -+ unsigned long hashed_id; -+ void* iam; -+ uint64_t* combined_bw; -+ struct ipt_bandwidth_info* non_const_self; -+ unsigned long* ref_count; -+ -+ -+}; -+#endif /*_IPT_BANDWIDTH_H*/ ---- /dev/null -+++ b/include/linux/netfilter_ipv4/ipt_timerange.h -@@ -0,0 +1,43 @@ -+/* timerange -- An iptables extension to match multiple timeranges within a week -+ * Originally designed for use with Gargoyle router firmware (gargoyle-router.com) -+ * -+ * -+ * Copyright © 2009 by Eric Bishop -+ * -+ * This file is free software: you may copy, redistribute and/or modify it -+ * under the terms of the GNU General Public License as published by the -+ * Free Software Foundation, either version 2 of the License, or (at your -+ * option) any later version. -+ * -+ * This file is distributed in the hope that it will be useful, but -+ * WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+ -+ -+ -+ -+#ifndef _IPT_TIMERANGE_H -+#define _IPT_TIMERANGE_H -+ -+ -+#define RANGE_LENGTH 51 -+ -+#define HOURS 1 -+#define WEEKDAYS 2 -+#define DAYS_HOURS (HOURS+WEEKDAYS) -+#define WEEKLY_RANGE 4 -+ -+ -+struct ipt_timerange_info -+{ -+ long ranges[RANGE_LENGTH]; -+ char days[7]; -+ char type; -+ unsigned char invert; -+}; -+#endif /*_IPT_TIMERANGE_H*/ ---- /dev/null -+++ b/include/linux/netfilter_ipv4/ipt_webmon.h -@@ -0,0 +1,63 @@ -+/* webmon -- A netfilter module to match URLs in HTTP requests -+ * This module can match using string match or regular expressions -+ * Originally designed for use with Gargoyle router firmware (gargoyle-router.com) -+ * -+ * -+ * Copyright © 2008-2010 by Eric Bishop -+ * -+ * This file is free software: you may copy, redistribute and/or modify it -+ * under the terms of the GNU General Public License as published by the -+ * Free Software Foundation, either version 2 of the License, or (at your -+ * option) any later version. -+ * -+ * This file is distributed in the hope that it will be useful, but -+ * WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+ -+ -+ -+ -+#ifndef _IPT_WEBMON_H -+#define _IPT_WEBMON_H -+ -+ -+#define WEBMON_MAX_IPS 256 -+#define WEBMON_MAX_IP_RANGES 16 -+ -+#define WEBMON_EXCLUDE 1 -+#define WEBMON_INCLUDE 2 -+ -+#define WEBMON_MAXDOMAIN 4 -+#define WEBMON_MAXSEARCH 8 -+ -+#define WEBMON_DOMAIN 16 -+#define WEBMON_SEARCH 32 -+ -+ -+#define WEBMON_SET 3064 -+ -+struct ipt_webmon_ip_range -+{ -+ uint32_t start; -+ uint32_t end; -+}; -+ -+struct ipt_webmon_info -+{ -+ uint32_t max_domains; -+ uint32_t max_searches; -+ uint32_t exclude_ips[WEBMON_MAX_IPS]; -+ struct ipt_webmon_ip_range exclude_ranges[WEBMON_MAX_IP_RANGES]; -+ uint32_t num_exclude_ips; -+ uint32_t num_exclude_ranges; -+ unsigned char exclude_type; -+ uint32_t* ref_count; -+ -+}; -+ -+#endif /*_IPT_WEBMON_H*/ ---- /dev/null -+++ b/include/linux/netfilter_ipv4/ipt_weburl.h -@@ -0,0 +1,45 @@ -+/* weburl -- A netfilter module to match URLs in HTTP requests -+ * This module can match using string match or regular expressions -+ * Originally designed for use with Gargoyle router firmware (gargoyle-router.com) -+ * -+ * -+ * Copyright © 2008 by Eric Bishop -+ * -+ * This file is free software: you may copy, redistribute and/or modify it -+ * under the terms of the GNU General Public License as published by the -+ * Free Software Foundation, either version 2 of the License, or (at your -+ * option) any later version. -+ * -+ * This file is distributed in the hope that it will be useful, but -+ * WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+ -+ -+ -+ -+#ifndef _IPT_WEBURL_H -+#define _IPT_WEBURL_H -+ -+ -+#define MAX_TEST_STR 1024 -+ -+#define WEBURL_CONTAINS_TYPE 1 -+#define WEBURL_REGEX_TYPE 2 -+#define WEBURL_EXACT_TYPE 3 -+#define WEBURL_ALL_PART 4 -+#define WEBURL_DOMAIN_PART 5 -+#define WEBURL_PATH_PART 6 -+ -+struct ipt_weburl_info -+{ -+ char test_str[MAX_TEST_STR]; -+ unsigned char match_type; -+ unsigned char match_part; -+ unsigned char invert; -+}; -+#endif /*_IPT_WEBURL_H*/ ---- a/net/ipv4/netfilter/Kconfig -+++ b/net/ipv4/netfilter/Kconfig -@@ -417,5 +417,25 @@ config IP_NF_ARP_MANGLE - - endif # IP_NF_ARPTABLES - -+config IP_NF_MATCH_BANDWIDTH -+ tristate "bandwidth match support" -+ depends on IP_NF_IPTABLES -+ help -+ This option enables bandwidth match support. -+config IP_NF_MATCH_TIMERANGE -+ tristate "timerange match support" -+ depends on IP_NF_IPTABLES -+ help -+ This option enables timerange match support. -+config IP_NF_MATCH_WEBMON -+ tristate "webmon match support" -+ depends on IP_NF_IPTABLES -+ help -+ This option enables webmon match support. -+config IP_NF_MATCH_WEBURL -+ tristate "weburl match support" -+ depends on IP_NF_IPTABLES -+ help -+ This option enables weburl match support. - endmenu - ---- a/net/ipv4/netfilter/Makefile -+++ b/net/ipv4/netfilter/Makefile -@@ -55,6 +55,10 @@ obj-$(CONFIG_IP_NF_RAW) += iptable_raw.o - obj-$(CONFIG_IP_NF_SECURITY) += iptable_security.o - - # matches -+obj-$(CONFIG_IP_NF_MATCH_BANDWIDTH) += ipt_bandwidth.o -+obj-$(CONFIG_IP_NF_MATCH_TIMERANGE) += ipt_timerange.o -+obj-$(CONFIG_IP_NF_MATCH_WEBMON) += ipt_webmon.o -+obj-$(CONFIG_IP_NF_MATCH_WEBURL) += ipt_weburl.o - obj-$(CONFIG_IP_NF_MATCH_AH) += ipt_ah.o - obj-$(CONFIG_IP_NF_MATCH_RPFILTER) += ipt_rpfilter.o - ---- /dev/null -+++ b/net/ipv4/netfilter/bandwidth_deps/tree_map.h -@@ -0,0 +1,1093 @@ -+/* -+ * Copyright © 2008 by Eric Bishop -+ * -+ * This work 'as-is' we provide. -+ * No warranty, express or implied. -+ * We've done our best, -+ * to debug and test. -+ * Liability for damages denied. -+ * -+ * Permission is granted hereby, -+ * to copy, share, and modify. -+ * Use as is fit, -+ * free or for profit. -+ * On this notice these rights rely. -+ * -+ * -+ * -+ * Note that unlike other portions of Gargoyle this code -+ * does not fall under the GPL, but the rather whimsical -+ * 'Poetic License' above. -+ * -+ * Basically, this library contains a bunch of utilities -+ * that I find useful. I'm sure other libraries exist -+ * that are just as good or better, but I like these tools -+ * because I personally wrote them, so I know their quirks. -+ * (i.e. I know where the bodies are buried). I want to -+ * make sure that I can re-use these utilities for whatever -+ * code I may want to write in the future be it -+ * proprietary or open-source, so I've put them under -+ * a very, very permissive license. -+ * -+ * If you find this code useful, use it. If not, don't. -+ * I really don't care. -+ * -+ */ -+ -+ -+#if __KERNEL__ -+ #define malloc(foo) kmalloc(foo,GFP_ATOMIC) -+ #define free(foo) kfree(foo) -+ #define printf(format,args...) printk(format,##args) -+ -+ /* kernel strdup */ -+ static inline char *kernel_strdup(const char *str); -+ static inline char *kernel_strdup(const char *str) -+ { -+ char *tmp; -+ long int s; -+ s=strlen(str) + 1; -+ tmp = kmalloc(s, GFP_ATOMIC); -+ if (tmp != NULL) -+ { -+ memcpy(tmp, str, s); -+ } -+ return tmp; -+ } -+ #define strdup kernel_strdup -+ -+#endif -+ -+ -+ -+/* tree_map structs / prototypes */ -+typedef struct long_tree_map_node -+{ -+ unsigned long key; -+ void* value; -+ -+ signed char balance; -+ struct long_tree_map_node* left; -+ struct long_tree_map_node* right; -+} long_map_node; -+ -+typedef struct -+{ -+ long_map_node* root; -+ unsigned long num_elements; -+ -+}long_map; -+ -+typedef struct -+{ -+ long_map lm; -+ unsigned char store_keys; -+ unsigned long num_elements; -+ -+}string_map; -+ -+ -+ -+/* long map functions */ -+long_map* initialize_long_map(void); -+void* get_long_map_element(long_map* map, unsigned long key); -+void* get_smallest_long_map_element(long_map* map, unsigned long* smallest_key); -+void* get_largest_long_map_element(long_map* map, unsigned long* largest_key); -+void* remove_smallest_long_map_element(long_map* map, unsigned long* smallest_key); -+void* remove_largest_long_map_element(long_map* map, unsigned long* largest_key); -+void* set_long_map_element(long_map* map, unsigned long key, void* value); -+void* remove_long_map_element(long_map* map, unsigned long key); -+unsigned long* get_sorted_long_map_keys(long_map* map, unsigned long* num_keys_returned); -+void** get_sorted_long_map_values(long_map* map, unsigned long* num_values_returned); -+void** destroy_long_map(long_map* map, int destruction_type, unsigned long* num_destroyed); -+void apply_to_every_long_map_value(long_map* map, void (*apply_func)(unsigned long key, void* value)); -+ -+/* string map functions */ -+string_map* initialize_string_map(unsigned char store_keys); -+void* get_string_map_element(string_map* map, const char* key); -+void* get_string_map_element_with_hashed_key(string_map* map, unsigned long hashed_key); -+void* set_string_map_element(string_map* map, const char* key, void* value); -+void* remove_string_map_element(string_map* map, const char* key); -+char** get_string_map_keys(string_map* map, unsigned long* num_keys_returned); -+void** get_string_map_values(string_map* map, unsigned long* num_values_returned); -+void** destroy_string_map(string_map* map, int destruction_type, unsigned long* num_destroyed); -+void apply_to_every_string_map_value(string_map* map, void (*apply_func)(char* key, void* value)); -+ -+ -+/* -+ * three different ways to deal with values when data structure is destroyed -+ */ -+#define DESTROY_MODE_RETURN_VALUES 20 -+#define DESTROY_MODE_FREE_VALUES 21 -+#define DESTROY_MODE_IGNORE_VALUES 22 -+ -+ -+/* -+ * for convenience & backwards compatibility alias _string_map_ functions to -+ * _map_ functions since string map is used more often than long map -+ */ -+#define initialize_map initialize_string_map -+#define set_map_element set_string_map_element -+#define get_map_element get_string_map_element -+#define remove_map_element remove_string_map_element -+#define get_map_keys get_string_map_keys -+#define get_map_values get_string_map_values -+#define destroy_map destroy_string_map -+ -+ -+/* internal utility structures/ functions */ -+typedef struct stack_node_struct -+{ -+ long_map_node** node_ptr; -+ signed char direction; -+ struct stack_node_struct* previous; -+} stack_node; -+ -+static void free_stack(stack_node* stack); -+static void** destroy_long_map_values(long_map* map, int destruction_type, unsigned long* num_destroyed); -+static void apply_to_every_long_map_node(long_map_node* node, void (*apply_func)(unsigned long key, void* value)); -+static void apply_to_every_string_map_node(long_map_node* node, unsigned char has_key, void (*apply_func)(char* key, void* value)); -+static void get_sorted_node_keys(long_map_node* node, unsigned long* key_list, unsigned long* next_key_index, int depth); -+static void get_sorted_node_values(long_map_node* node, void** value_list, unsigned long* next_value_index, int depth); -+static signed char rebalance (long_map_node** n, signed char direction, signed char update_op); -+static void rotate_right (long_map_node** parent); -+static void rotate_left (long_map_node** parent); -+ -+/* internal for string map */ -+typedef struct -+{ -+ char* key; -+ void* value; -+} string_map_key_value; -+static unsigned long sdbm_string_hash(const char *key); -+ -+ -+ -+ -+/*************************************************** -+ * For testing only -+ ***************************************************/ -+/* -+void print_list(stack_node *l); -+ -+void print_list(stack_node *l) -+{ -+ if(l != NULL) -+ { -+ printf(" list key = %ld, dir=%d, \n", (*(l->node_ptr))->key, l->direction); -+ print_list(l->previous); -+ } -+} -+*/ -+/****************************************************** -+ * End testing Code -+ *******************************************************/ -+ -+ -+ -+ -+/*************************************************** -+ * string_map function definitions -+ ***************************************************/ -+ -+string_map* initialize_string_map(unsigned char store_keys) -+{ -+ string_map* map = (string_map*)malloc(sizeof(string_map)); -+ if(map != NULL) -+ { -+ map->store_keys = store_keys; -+ map->lm.root = NULL; -+ map->lm.num_elements = 0; -+ map->num_elements = map->lm.num_elements; -+ } -+ return map; -+} -+ -+void* get_string_map_element(string_map* map, const char* key) -+{ -+ unsigned long hashed_key = sdbm_string_hash(key); -+ -+ return get_string_map_element_with_hashed_key(map, hashed_key); -+} -+ -+void* get_string_map_element_with_hashed_key(string_map* map, unsigned long hashed_key) -+{ -+ void* return_value; -+ /* printk("doing lookup for key = %lu\n", hashed_key); */ -+ return_value = get_long_map_element( &(map->lm), hashed_key); -+ if(return_value != NULL && map->store_keys) -+ { -+ string_map_key_value* r = (string_map_key_value*)return_value; -+ return_value = r->value; -+ } -+ map->num_elements = map->lm.num_elements; -+ return return_value; -+} -+ -+void* set_string_map_element(string_map* map, const char* key, void* value) -+{ -+ unsigned long hashed_key = sdbm_string_hash(key); -+ void* return_value = NULL; -+ if(map->store_keys) -+ { -+ string_map_key_value* kv = (string_map_key_value*)malloc(sizeof(string_map_key_value)); -+ if(kv == NULL) /* deal with malloc failure */ -+ { -+ return NULL; -+ } -+ kv->key = strdup(key); -+ if(kv->key == NULL) /* deal with malloc failure */ -+ { -+ free(kv); -+ return NULL; -+ } -+ kv->value = value; -+ return_value = set_long_map_element( &(map->lm), hashed_key, kv); -+ if(return_value != NULL) -+ { -+ string_map_key_value* r = (string_map_key_value*)return_value; -+ return_value = r->value; -+ free(r->key); -+ free(r); -+ } -+ } -+ else -+ { -+ return_value = set_long_map_element( &(map->lm), hashed_key, value); -+ } -+ map->num_elements = map->lm.num_elements; -+ return return_value; -+} -+ -+void* remove_string_map_element(string_map* map, const char* key) -+{ -+ unsigned long hashed_key = sdbm_string_hash(key); -+ void* return_value = remove_long_map_element( &(map->lm), hashed_key); -+ -+ if(return_value != NULL && map->store_keys) -+ { -+ string_map_key_value* r = (string_map_key_value*)return_value; -+ return_value = r->value; -+ free(r->key); -+ free(r); -+ } -+ map->num_elements = map->lm.num_elements; -+ return return_value; -+} -+ -+char** get_string_map_keys(string_map* map, unsigned long* num_keys_returned) -+{ -+ char** str_keys; -+ str_keys = (char**)malloc((map->num_elements+1)*sizeof(char*)); -+ if(str_keys == NULL) /* deal with malloc failure */ -+ { -+ return NULL; -+ } -+ str_keys[0] = NULL; -+ *num_keys_returned = 0; -+ if(map->store_keys && map->num_elements > 0) -+ { -+ unsigned long list_length; -+ void** long_values = get_sorted_long_map_values( &(map->lm), &list_length); -+ unsigned long key_index; -+ /*list_length will be 0 on malloc failure in get_sorted_long_map_values, so this code shouldn't seg fault if that happens */ -+ for(key_index = 0; key_index < list_length; key_index++) -+ { -+ str_keys[key_index] = strdup( ((string_map_key_value*)(long_values[key_index]))->key); -+ if(str_keys[key_index] == NULL) /* deal with malloc failure */ -+ { -+ //just return the incomplete list (hey, it's null terminated...) -+ free(long_values); -+ return str_keys; -+ } -+ *num_keys_returned = *num_keys_returned + 1; -+ } -+ str_keys[list_length] = NULL; -+ free(long_values); -+ } -+ return str_keys; -+} -+ -+ -+void** get_string_map_values(string_map* map, unsigned long* num_values_returned) -+{ -+ void** values = NULL; -+ if(map != NULL) -+ { -+ values = get_sorted_long_map_values ( &(map->lm), num_values_returned ); -+ } -+ return values; -+} -+ -+ -+void** destroy_string_map(string_map* map, int destruction_type, unsigned long* num_destroyed) -+{ -+ void** return_values = NULL; -+ if(map != NULL) -+ { -+ if(map->store_keys) -+ { -+ void** kvs = destroy_long_map_values( &(map->lm), DESTROY_MODE_RETURN_VALUES, num_destroyed ); -+ unsigned long kv_index = 0; -+ for(kv_index=0; kv_index < *num_destroyed; kv_index++) -+ { -+ string_map_key_value* kv = (string_map_key_value*)kvs[kv_index]; -+ void* value = kv->value; -+ -+ free(kv->key); -+ free(kv); -+ if(destruction_type == DESTROY_MODE_FREE_VALUES) -+ { -+ free(value); -+ } -+ if(destruction_type == DESTROY_MODE_RETURN_VALUES) -+ { -+ kvs[kv_index] = value; -+ } -+ } -+ if(destruction_type == DESTROY_MODE_RETURN_VALUES) -+ { -+ return_values = kvs; -+ } -+ else -+ { -+ free(kvs); -+ } -+ } -+ else -+ { -+ return_values = destroy_long_map_values( &(map->lm), destruction_type, num_destroyed ); -+ } -+ free(map); -+ } -+ return return_values; -+} -+ -+ -+ -+ -+/*************************************************** -+ * long_map function definitions -+ ***************************************************/ -+ -+long_map* initialize_long_map(void) -+{ -+ long_map* map = (long_map*)malloc(sizeof(long_map)); -+ if(map != NULL) /* test for malloc failure */ -+ { -+ map->root = NULL; -+ map->num_elements = 0; -+ } -+ return map; -+} -+ -+void* get_long_map_element(long_map* map, unsigned long key) -+{ -+ void* value = NULL; -+ -+ if(map->root != NULL) -+ { -+ long_map_node* parent_node = map->root; -+ long_map_node* next_node; -+ while( key != parent_node->key && (next_node = (long_map_node *)(key < parent_node->key ? parent_node->left : parent_node->right)) != NULL) -+ { -+ parent_node = next_node; -+ } -+ if(parent_node->key == key) -+ { -+ value = parent_node->value; -+ } -+ } -+ return value; -+} -+ -+void* get_smallest_long_map_element(long_map* map, unsigned long* smallest_key) -+{ -+ void* value = NULL; -+ if(map->root != NULL) -+ { -+ long_map_node* next_node = map->root; -+ while( next_node->left != NULL) -+ { -+ next_node = next_node->left; -+ } -+ value = next_node->value; -+ *smallest_key = next_node->key; -+ } -+ return value; -+} -+ -+void* get_largest_long_map_element(long_map* map, unsigned long* largest_key) -+{ -+ void* value = NULL; -+ if(map->root != NULL) -+ { -+ long_map_node* next_node = map->root; -+ while( next_node->right != NULL) -+ { -+ next_node = next_node->right; -+ } -+ value = next_node->value; -+ *largest_key = next_node->key; -+ } -+ return value; -+} -+ -+void* remove_smallest_long_map_element(long_map* map, unsigned long* smallest_key) -+{ -+ get_smallest_long_map_element(map, smallest_key); -+ return remove_long_map_element(map, *smallest_key); -+} -+ -+void* remove_largest_long_map_element(long_map* map, unsigned long* largest_key) -+{ -+ get_largest_long_map_element(map, largest_key); -+ return remove_long_map_element(map, *largest_key); -+} -+ -+ -+/* if replacement performed, returns replaced value, otherwise null */ -+void* set_long_map_element(long_map* map, unsigned long key, void* value) -+{ -+ stack_node* parent_list = NULL; -+ void* old_value = NULL; -+ int old_value_found = 0; -+ -+ long_map_node* parent_node; -+ long_map_node* next_node; -+ stack_node* next_parent; -+ stack_node* previous_parent; -+ signed char new_balance; -+ -+ -+ long_map_node* new_node = (long_map_node*)malloc(sizeof(long_map_node)); -+ if(new_node == NULL) -+ { -+ return NULL; -+ } -+ new_node->value = value; -+ new_node->key = key; -+ new_node->left = NULL; -+ new_node->right = NULL; -+ new_node->balance = 0; -+ -+ -+ -+ if(map->root == NULL) -+ { -+ map->root = new_node; -+ } -+ else -+ { -+ parent_node = map->root; -+ -+ next_parent = (stack_node*)malloc(sizeof(stack_node)); -+ if(next_parent == NULL) /* deal with malloc failure */ -+ { -+ free(new_node); -+ return NULL; /* won't insert but won't seg fault */ -+ } -+ next_parent->node_ptr = &(map->root); -+ next_parent->previous = parent_list; -+ parent_list = next_parent; -+ -+ while( key != parent_node->key && (next_node = (key < parent_node->key ? parent_node->left : parent_node->right) ) != NULL) -+ { -+ next_parent = (stack_node*)malloc(sizeof(stack_node)); -+ if(next_parent == NULL) /* deal with malloc failure */ -+ { -+ /* free previous stack nodes to prevent memory leak */ -+ free_stack(parent_list); -+ free(new_node); -+ return NULL; -+ } -+ next_parent->node_ptr = key < parent_node->key ? &(parent_node->left) : &(parent_node->right); -+ next_parent->previous = parent_list; -+ next_parent->previous->direction = key < parent_node->key ? -1 : 1; -+ parent_list = next_parent; -+ -+ parent_node = next_node; -+ } -+ -+ -+ if(key == parent_node->key) -+ { -+ old_value = parent_node->value; -+ old_value_found = 1; -+ parent_node->value = value; -+ free(new_node); -+ /* we merely replaced a node, no need to rebalance */ -+ } -+ else -+ { -+ if(key < parent_node->key) -+ { -+ parent_node->left = (void*)new_node; -+ parent_list->direction = -1; -+ } -+ else -+ { -+ parent_node->right = (void*)new_node; -+ parent_list->direction = 1; -+ } -+ -+ -+ /* we inserted a node, rebalance */ -+ previous_parent = parent_list; -+ new_balance = 1; /* initial value is not used, but must not be 0 for initial loop condition */ -+ -+ -+ while(previous_parent != NULL && new_balance != 0) -+ { -+ new_balance = rebalance(previous_parent->node_ptr, previous_parent->direction, 1); -+ previous_parent = previous_parent->previous; -+ } -+ } -+ } -+ -+ free_stack(parent_list); -+ -+ if(old_value_found == 0) -+ { -+ map->num_elements = map->num_elements + 1; -+ } -+ -+ return old_value; -+} -+ -+ -+void* remove_long_map_element(long_map* map, unsigned long key) -+{ -+ -+ void* value = NULL; -+ -+ long_map_node* root_node = map->root; -+ stack_node* parent_list = NULL; -+ -+ -+ long_map_node* remove_parent; -+ long_map_node* remove_node; -+ long_map_node* next_node; -+ -+ long_map_node* replacement; -+ long_map_node* replacement_parent; -+ long_map_node* replacement_next; -+ -+ stack_node* next_parent; -+ stack_node* previous_parent; -+ stack_node* replacement_stack_node; -+ -+ -+ signed char new_balance; -+ -+ -+ -+ if(root_node != NULL) -+ { -+ remove_parent = root_node; -+ remove_node = key < remove_parent->key ? remove_parent->left : remove_parent->right; -+ -+ if(remove_node != NULL && key != remove_parent->key) -+ { -+ next_parent = (stack_node*)malloc(sizeof(stack_node)); -+ if(next_parent == NULL) /* deal with malloc failure */ -+ { -+ return NULL; -+ } -+ next_parent->node_ptr = &(map->root); -+ next_parent->previous = parent_list; -+ parent_list = next_parent; -+ while( key != remove_node->key && (next_node = (key < remove_node->key ? remove_node->left : remove_node->right)) != NULL) -+ { -+ next_parent = (stack_node*)malloc(sizeof(stack_node)); -+ if(next_parent == NULL) /* deal with malloc failure */ -+ { -+ /* free previous stack nodes to prevent memory leak */ -+ free_stack(parent_list); -+ return NULL; -+ } -+ next_parent->node_ptr = key < remove_parent->key ? &(remove_parent->left) : &(remove_parent->right); -+ next_parent->previous = parent_list; -+ next_parent->previous->direction = key < remove_parent->key ? -1 : 1; -+ parent_list = next_parent; -+ -+ -+ remove_parent = remove_node; -+ remove_node = next_node; -+ } -+ parent_list->direction = key < remove_parent-> key ? -1 : 1; -+ } -+ else -+ { -+ remove_node = remove_parent; -+ } -+ -+ -+ if(key == remove_node->key) -+ { -+ -+ /* find replacement for node we are deleting */ -+ if( remove_node->right == NULL ) -+ { -+ replacement = remove_node->left; -+ } -+ else if( remove_node->right->left == NULL) -+ { -+ -+ replacement = remove_node->right; -+ replacement->left = remove_node->left; -+ replacement->balance = remove_node->balance; -+ -+ /* put pointer to replacement node into list for balance update */ -+ replacement_stack_node = (stack_node*)malloc(sizeof(stack_node)); -+ if(replacement_stack_node == NULL) /* deal with malloc failure */ -+ { -+ /* free previous stack nodes to prevent memory leak */ -+ free_stack(parent_list); -+ return NULL; -+ } -+ replacement_stack_node->previous = parent_list; -+ replacement_stack_node->direction = 1; /* replacement is from right */ -+ if(remove_node == remove_parent) /* special case for root node */ -+ { -+ replacement_stack_node->node_ptr = &(map->root); -+ } -+ else -+ { -+ replacement_stack_node->node_ptr = key < remove_parent-> key ? &(remove_parent->left) : &(remove_parent->right); -+ } -+ parent_list = replacement_stack_node; -+ -+ } -+ else -+ { -+ /* put pointer to replacement node into list for balance update */ -+ replacement_stack_node = (stack_node*)malloc(sizeof(stack_node)); -+ if(replacement_stack_node == NULL) /* deal with malloc failure */ -+ { -+ /* free previous stack nodes to prevent memory leak */ -+ free_stack(parent_list); -+ return NULL; -+ } -+ -+ replacement_stack_node->previous = parent_list; -+ replacement_stack_node->direction = 1; /* we always look for replacement on right */ -+ if(remove_node == remove_parent) /* special case for root node */ -+ { -+ replacement_stack_node->node_ptr = &(map->root); -+ } -+ else -+ { -+ replacement_stack_node->node_ptr = key < remove_parent-> key ? &(remove_parent->left) : &(remove_parent->right); -+ } -+ -+ parent_list = replacement_stack_node; -+ -+ -+ /* -+ * put pointer to replacement node->right into list for balance update -+ * this node will have to be updated with the proper pointer -+ * after we have identified the replacement -+ */ -+ replacement_stack_node = (stack_node*)malloc(sizeof(stack_node)); -+ if(replacement_stack_node == NULL) /* deal with malloc failure */ -+ { -+ /* free previous stack nodes to prevent memory leak */ -+ free_stack(parent_list); -+ return NULL; -+ } -+ -+ replacement_stack_node->previous = parent_list; -+ replacement_stack_node->direction = -1; /* we always look for replacement to left of this node */ -+ parent_list = replacement_stack_node; -+ -+ /* find smallest node on right (large) side of tree */ -+ replacement_parent = remove_node->right; -+ replacement = replacement_parent->left; -+ -+ while((replacement_next = replacement->left) != NULL) -+ { -+ next_parent = (stack_node*)malloc(sizeof(stack_node)); -+ if(next_parent == NULL) /* deal with malloc failure */ -+ { -+ /* free previous stack nodes to prevent memory leak */ -+ free_stack(parent_list); -+ return NULL; -+ } -+ -+ next_parent->node_ptr = &(replacement_parent->left); -+ next_parent->previous = parent_list; -+ next_parent->direction = -1; /* we always go left */ -+ parent_list = next_parent; -+ -+ replacement_parent = replacement; -+ replacement = replacement_next; -+ -+ } -+ -+ replacement_parent->left = replacement->right; -+ -+ replacement->left = remove_node->left; -+ replacement->right = remove_node->right; -+ replacement->balance = remove_node->balance; -+ replacement_stack_node->node_ptr = &(replacement->right); -+ } -+ -+ /* insert replacement at proper location in tree */ -+ if(remove_node == remove_parent) -+ { -+ map->root = replacement; -+ } -+ else -+ { -+ remove_parent->left = remove_node == remove_parent->left ? replacement : remove_parent->left; -+ remove_parent->right = remove_node == remove_parent->right ? replacement : remove_parent->right; -+ } -+ -+ -+ /* rebalance tree */ -+ previous_parent = parent_list; -+ new_balance = 0; -+ while(previous_parent != NULL && new_balance == 0) -+ { -+ new_balance = rebalance(previous_parent->node_ptr, previous_parent->direction, -1); -+ previous_parent = previous_parent->previous; -+ } -+ -+ -+ -+ -+ /* -+ * since we found a value to remove, decrease number of elements in map -+ * set return value to the deleted node's value and free the node -+ */ -+ map->num_elements = map->num_elements - 1; -+ value = remove_node->value; -+ free(remove_node); -+ } -+ } -+ -+ free_stack(parent_list); -+ -+ return value; -+} -+ -+ -+/* note: returned keys are dynamically allocated, you need to free them! */ -+unsigned long* get_sorted_long_map_keys(long_map* map, unsigned long* num_keys_returned) -+{ -+ unsigned long* key_list = (unsigned long*)malloc((map->num_elements)*sizeof(unsigned long)); -+ unsigned long next_key_index; -+ if(key_list == NULL) -+ { -+ *num_keys_returned = 0; -+ return NULL; -+ } -+ next_key_index = 0; -+ get_sorted_node_keys(map->root, key_list, &next_key_index, 0); -+ -+ *num_keys_returned = map->num_elements; -+ -+ return key_list; -+} -+ -+ -+void** get_sorted_long_map_values(long_map* map, unsigned long* num_values_returned) -+{ -+ void** value_list = (void**)malloc((map->num_elements+1)*sizeof(void*)); -+ unsigned long next_value_index; -+ -+ if(value_list == NULL) -+ { -+ *num_values_returned = 0; -+ return NULL; -+ } -+ next_value_index = 0; -+ get_sorted_node_values(map->root, value_list, &next_value_index, 0); -+ value_list[map->num_elements] = NULL; /* since we're dealing with pointers make list null terminated */ -+ -+ *num_values_returned = map->num_elements; -+ return value_list; -+ -+} -+ -+ -+ -+void** destroy_long_map(long_map* map, int destruction_type, unsigned long* num_destroyed) -+{ -+ void** return_values = destroy_long_map_values(map, destruction_type, num_destroyed); -+ free(map); -+ return return_values; -+} -+ -+ -+ -+void apply_to_every_long_map_value(long_map* map, void (*apply_func)(unsigned long key, void* value)) -+{ -+ apply_to_every_long_map_node(map->root, apply_func); -+} -+void apply_to_every_string_map_value(string_map* map, void (*apply_func)(char* key, void* value)) -+{ -+ apply_to_every_string_map_node( (map->lm).root, map->store_keys, apply_func); -+} -+ -+ -+/*************************************************** -+ * internal utility function definitions -+ ***************************************************/ -+static void free_stack(stack_node* stack) -+{ -+ while(stack != NULL) -+ { -+ stack_node* prev_node = stack; -+ stack = prev_node->previous; -+ free(prev_node); -+ } -+ -+} -+ -+static void** destroy_long_map_values(long_map* map, int destruction_type, unsigned long* num_destroyed) -+{ -+ void** return_values = NULL; -+ unsigned long return_index = 0; -+ -+ *num_destroyed = 0; -+ -+ if(destruction_type == DESTROY_MODE_RETURN_VALUES) -+ { -+ return_values = (void**)malloc((map->num_elements+1)*sizeof(void*)); -+ if(return_values == NULL) /* deal with malloc failure */ -+ { -+ destruction_type = DESTROY_MODE_IGNORE_VALUES; /* could cause memory leak, but there's no other way to be sure we won't seg fault */ -+ } -+ else -+ { -+ return_values[map->num_elements] = NULL; -+ } -+ } -+ while(map->num_elements > 0) -+ { -+ unsigned long smallest_key; -+ void* removed_value = remove_smallest_long_map_element(map, &smallest_key); -+ if(destruction_type == DESTROY_MODE_RETURN_VALUES) -+ { -+ return_values[return_index] = removed_value; -+ } -+ if(destruction_type == DESTROY_MODE_FREE_VALUES) -+ { -+ free(removed_value); -+ } -+ return_index++; -+ *num_destroyed = *num_destroyed + 1; -+ } -+ return return_values; -+} -+ -+static void apply_to_every_long_map_node(long_map_node* node, void (*apply_func)(unsigned long key, void* value)) -+{ -+ if(node != NULL) -+ { -+ apply_to_every_long_map_node(node->left, apply_func); -+ -+ apply_func(node->key, node->value); -+ -+ apply_to_every_long_map_node(node->right, apply_func); -+ } -+} -+static void apply_to_every_string_map_node(long_map_node* node, unsigned char has_key, void (*apply_func)(char* key, void* value)) -+{ -+ if(node != NULL) -+ { -+ apply_to_every_string_map_node(node->left, has_key, apply_func); -+ -+ if(has_key) -+ { -+ string_map_key_value* kv = (string_map_key_value*)(node->value); -+ apply_func(kv->key, kv->value); -+ } -+ else -+ { -+ apply_func(NULL, node->value); -+ } -+ apply_to_every_string_map_node(node->right, has_key, apply_func); -+ } -+} -+ -+ -+ -+static void get_sorted_node_keys(long_map_node* node, unsigned long* key_list, unsigned long* next_key_index, int depth) -+{ -+ if(node != NULL) -+ { -+ get_sorted_node_keys(node->left, key_list, next_key_index, depth+1); -+ -+ key_list[ *next_key_index ] = node->key; -+ (*next_key_index)++; -+ -+ get_sorted_node_keys(node->right, key_list, next_key_index, depth+1); -+ } -+} -+ -+static void get_sorted_node_values(long_map_node* node, void** value_list, unsigned long* next_value_index, int depth) -+{ -+ if(node != NULL) -+ { -+ get_sorted_node_values(node->left, value_list, next_value_index, depth+1); -+ -+ value_list[ *next_value_index ] = node->value; -+ (*next_value_index)++; -+ -+ get_sorted_node_values(node->right, value_list, next_value_index, depth+1); -+ } -+} -+ -+ -+ -+/* -+ * direction = -1 indicates left subtree updated, direction = 1 for right subtree -+ * update_op = -1 indicates delete node, update_op = 1 for insert node -+ */ -+static signed char rebalance (long_map_node** n, signed char direction, signed char update_op) -+{ -+ /* -+ printf( "original: key = %ld, balance = %d, update_op=%d, direction=%d\n", (*n)->key, (*n)->balance, update_op, direction); -+ */ -+ -+ (*n)->balance = (*n)->balance + (update_op*direction); -+ -+ if( (*n)->balance < -1) -+ { -+ if((*n)->left->balance < 0) -+ { -+ rotate_right(n); -+ (*n)->right->balance = 0; -+ (*n)->balance = 0; -+ } -+ else if((*n)->left->balance == 0) -+ { -+ rotate_right(n); -+ (*n)->right->balance = -1; -+ (*n)->balance = 1; -+ } -+ else if((*n)->left->balance > 0) -+ { -+ rotate_left( &((*n)->left) ); -+ rotate_right(n); -+ /* -+ if( (*n)->balance < 0 ) -+ { -+ (*n)->left->balance = 0; -+ (*n)->right->balance = 1; -+ } -+ else if( (*n)->balance == 0 ) -+ { -+ (*n)->left->balance = 0; -+ (*n)->right->balance = 0; -+ } -+ else if( (*n)->balance > 0 ) -+ { -+ (*n)->left->balance = -1; -+ (*n)->right->balance = 0; -+ } -+ */ -+ (*n)->left->balance = (*n)->balance > 0 ? -1 : 0; -+ (*n)->right->balance = (*n)->balance < 0 ? 1 : 0; -+ (*n)->balance = 0; -+ } -+ } -+ if( (*n)->balance > 1) -+ { -+ if((*n)->right->balance > 0) -+ { -+ rotate_left(n); -+ (*n)->left->balance = 0; -+ (*n)->balance = 0; -+ } -+ else if ((*n)->right->balance == 0) -+ { -+ rotate_left(n); -+ (*n)->left->balance = 1; -+ (*n)->balance = -1; -+ } -+ else if((*n)->right->balance < 0) -+ { -+ rotate_right( &((*n)->right) ); -+ rotate_left(n); -+ /* -+ if( (*n)->balance < 0 ) -+ { -+ (*n)->left->balance = 0; -+ (*n)->right->balance = 1; -+ } -+ else if( (*n)->balance == 0 ) -+ { -+ (*n)->left->balance = 0; -+ (*n)->right->balance = 0; -+ } -+ else if( (*n)->balance > 0 ) -+ { -+ (*n)->left->balance = -1; -+ (*n)->right->balance = 0; -+ } -+ */ -+ (*n)->left->balance = (*n)->balance > 0 ? -1 : 0; -+ (*n)->right->balance = (*n)->balance < 0 ? 1 : 0; -+ (*n)->balance = 0; -+ } -+ } -+ -+ /* -+ printf( "key = %ld, balance = %d\n", (*n)->key, (*n)->balance); -+ */ -+ -+ return (*n)->balance; -+} -+ -+ -+static void rotate_right (long_map_node** parent) -+{ -+ long_map_node* old_parent = *parent; -+ long_map_node* pivot = old_parent->left; -+ old_parent->left = pivot->right; -+ pivot->right = old_parent; -+ -+ *parent = pivot; -+} -+ -+static void rotate_left (long_map_node** parent) -+{ -+ long_map_node* old_parent = *parent; -+ long_map_node* pivot = old_parent->right; -+ old_parent->right = pivot->left; -+ pivot->left = old_parent; -+ -+ *parent = pivot; -+} -+ -+ -+ -+/*************************************************************************** -+ * This algorithm was created for the sdbm database library (a public-domain -+ * reimplementation of ndbm) and seems to work relatively well in -+ * scrambling bits -+ * -+ * -+ * This code was derived from code found at: -+ * http://www.cse.yorku.ca/~oz/hash.html -+ ***************************************************************************/ -+static unsigned long sdbm_string_hash(const char *key) -+{ -+ unsigned long hashed_key = 0; -+ -+ int index = 0; -+ unsigned int nextch; -+ while(key[index] != '\0') -+ { -+ nextch = key[index]; -+ hashed_key = nextch + (hashed_key << 6) + (hashed_key << 16) - hashed_key; -+ index++; -+ } -+ return hashed_key; -+} -+ -+ ---- /dev/null -+++ b/net/ipv4/netfilter/ipt_bandwidth.c -@@ -0,0 +1,2501 @@ -+/* bandwidth -- An iptables extension for bandwidth monitoring/control -+ * Can be used to efficiently monitor bandwidth and/or implement bandwidth quotas -+ * Can be queried using the iptbwctl userspace library -+ * Originally designed for use with Gargoyle router firmware (gargoyle-router.com) -+ * -+ * -+ * Copyright © 2009-2011 by Eric Bishop -+ * -+ * This file is free software: you may copy, redistribute and/or modify it -+ * under the terms of the GNU General Public License as published by the -+ * Free Software Foundation, either version 2 of the License, or (at your -+ * option) any later version. -+ * -+ * This file is distributed in the hope that it will be useful, but -+ * WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#include -+ -+ -+#include "bandwidth_deps/tree_map.h" -+#include -+#include -+ -+ -+#include -+#include -+ -+ -+/* #define BANDWIDTH_DEBUG 1 */ -+ -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Eric Bishop"); -+MODULE_DESCRIPTION("Match bandwidth used, designed for use with Gargoyle web interface (www.gargoyle-router.com)"); -+ -+/* -+ * WARNING: accessing the sys_tz variable takes FOREVER, and kills performance -+ * keep a local variable that gets updated from the extern variable -+ */ -+extern struct timezone sys_tz; -+static int local_minutes_west; -+static int local_seconds_west; -+static time_t last_local_mw_update; -+ -+ -+static spinlock_t bandwidth_lock = __SPIN_LOCK_UNLOCKED(bandwidth_lock); -+DEFINE_SEMAPHORE(userspace_lock); -+ -+static string_map* id_map = NULL; -+ -+ -+typedef struct info_and_maps_struct -+{ -+ struct ipt_bandwidth_info* info; -+ long_map* ip_map; -+ long_map* ip_history_map; -+}info_and_maps; -+ -+typedef struct history_struct -+{ -+ time_t first_start; -+ time_t first_end; -+ time_t last_end; /* also beginning of current time frame */ -+ uint32_t max_nodes; -+ uint32_t num_nodes; -+ uint32_t non_zero_nodes; -+ uint32_t current_index; -+ uint64_t* history_data; -+} bw_history; -+ -+ -+ -+static unsigned char set_in_progress = 0; -+static char set_id[BANDWIDTH_MAX_ID_LENGTH] = ""; -+ -+/* -+ * function prototypes -+ * -+ * (prototypes only provided for -+ * functions not part of iptables API) -+ * -+*/ -+ -+ -+static void adjust_ip_for_backwards_time_shift(unsigned long key, void* value); -+static void adjust_id_for_backwards_time_shift(char* key, void* value); -+static void check_for_backwards_time_shift(time_t now); -+ -+ -+static void shift_timezone_of_ip(unsigned long key, void* value); -+static void shift_timezone_of_id(char* key, void* value); -+static void check_for_timezone_shift(time_t now, int already_locked); -+ -+ -+ -+static bw_history* initialize_history(uint32_t max_nodes); -+static unsigned char update_history(bw_history* history, time_t interval_start, time_t interval_end, struct ipt_bandwidth_info* info); -+ -+ -+ -+static void do_reset(unsigned long key, void* value); -+static void set_bandwidth_to_zero(unsigned long key, void* value); -+static void handle_interval_reset(info_and_maps* iam, time_t now); -+ -+static uint64_t pow64(uint64_t base, uint64_t pow); -+static uint64_t get_bw_record_max(void); /* called by init to set global variable */ -+ -+static inline int is_leap(unsigned int y); -+static time_t get_next_reset_time(struct ipt_bandwidth_info *info, time_t now, time_t previous_reset); -+static time_t get_nominal_previous_reset_time(struct ipt_bandwidth_info *info, time_t current_next_reset); -+ -+static uint64_t* initialize_map_entries_for_ip(info_and_maps* iam, unsigned long ip, uint64_t initial_bandwidth); -+ -+ -+ -+ -+static time_t backwards_check = 0; -+static time_t backwards_adjust_current_time = 0; -+static time_t backwards_adjust_info_previous_reset = 0; -+static time_t backwards_adjust_ips_zeroed = 0; -+static info_and_maps* backwards_adjust_iam = NULL; -+ -+/* -+static char print_out_buf[25000]; -+static void print_to_buf(char* outdat); -+static void reset_buf(void); -+static void do_print_buf(void); -+ -+static void print_to_buf(char* outdat) -+{ -+ int buf_len = strlen(print_out_buf); -+ sprintf(print_out_buf+buf_len, "\t%s\n", outdat); -+} -+static void reset_buf(void) -+{ -+ print_out_buf[0] = '\n'; -+ print_out_buf[1] = '\0'; -+} -+static void do_print_buf(void) -+{ -+ char* start = print_out_buf; -+ char* next = strchr(start, '\n'); -+ while(next != NULL) -+ { -+ *next = '\0'; -+ printk("%s\n", start); -+ start = next+1; -+ next = strchr(start, '\n'); -+ } -+ printk("%s\n", start); -+ -+ reset_buf(); -+} -+*/ -+ -+static void adjust_ip_for_backwards_time_shift(unsigned long key, void* value) -+{ -+ bw_history* old_history = (bw_history*)value; -+ -+ if(old_history->num_nodes == 1) -+ { -+ if(backwards_adjust_info_previous_reset > backwards_adjust_current_time) -+ { -+ if(backwards_adjust_ips_zeroed == 0) -+ { -+ apply_to_every_long_map_value(backwards_adjust_iam->ip_map, set_bandwidth_to_zero); -+ backwards_adjust_iam->info->next_reset = get_next_reset_time(backwards_adjust_iam->info, backwards_adjust_current_time, backwards_adjust_current_time); -+ backwards_adjust_iam->info->previous_reset = backwards_adjust_current_time; -+ backwards_adjust_iam->info->current_bandwidth = 0; -+ backwards_adjust_ips_zeroed = 1; -+ } -+ } -+ return; -+ } -+ else if(old_history->last_end < backwards_adjust_current_time) -+ { -+ return; -+ } -+ else -+ { -+ -+ /* -+ * reconstruct new history without newest nodes, to represent data as it was -+ * last time the current time was set to the interval to which we just jumped back -+ */ -+ uint32_t next_old_index; -+ time_t old_next_start = old_history->first_start == 0 ? backwards_adjust_info_previous_reset : old_history->first_start; /* first time point in old history */ -+ bw_history* new_history = initialize_history(old_history->max_nodes); -+ if(new_history == NULL) -+ { -+ printk("ipt_bandwidth: warning, kmalloc failure!\n"); -+ return; -+ } -+ -+ -+ -+ /* oldest index in old history -- we iterate forward through old history using this index */ -+ next_old_index = old_history->num_nodes == old_history->max_nodes ? (old_history->current_index+1) % old_history->max_nodes : 0; -+ -+ -+ /* if first time point is after current time, just completely re-initialize history, otherwise set first time point to old first time point */ -+ (new_history->history_data)[ new_history->current_index ] = old_next_start < backwards_adjust_current_time ? (old_history->history_data)[next_old_index] : 0; -+ backwards_adjust_iam->info->previous_reset = old_next_start < backwards_adjust_current_time ? old_next_start : backwards_adjust_current_time; -+ -+ -+ /* iterate through old history, rebuilding in new history*/ -+ while( old_next_start < backwards_adjust_current_time ) -+ { -+ time_t old_next_end = get_next_reset_time(backwards_adjust_iam->info, old_next_start, old_next_start); /* 2nd param = last reset, 3rd param = current time */ -+ if( old_next_end < backwards_adjust_current_time) -+ { -+ update_history(new_history, old_next_start, old_next_end, backwards_adjust_iam->info); -+ next_old_index++; -+ (new_history->history_data)[ new_history->current_index ] = (old_history->history_data)[next_old_index]; -+ } -+ backwards_adjust_iam->info->previous_reset = old_next_start; /*update previous_reset variable in bw_info as we iterate */ -+ old_next_start = old_next_end; -+ } -+ -+ /* update next_reset variable from previous_reset variable which we've already set */ -+ backwards_adjust_iam->info->next_reset = get_next_reset_time(backwards_adjust_iam->info, backwards_adjust_iam->info->previous_reset, backwards_adjust_iam->info->previous_reset); -+ -+ -+ -+ /* set old_history to be new_history */ -+ kfree(old_history->history_data); -+ old_history->history_data = new_history->history_data; -+ old_history->first_start = new_history->first_start; -+ old_history->first_end = new_history->first_end; -+ old_history->last_end = new_history->last_end; -+ old_history->num_nodes = new_history->num_nodes; -+ old_history->non_zero_nodes = new_history->non_zero_nodes; -+ old_history->current_index = new_history->current_index; -+ set_long_map_element(backwards_adjust_iam->ip_map, key, (void*)(old_history->history_data + old_history->current_index) ); -+ if(key == 0) -+ { -+ backwards_adjust_iam->info->combined_bw = (uint64_t*)(old_history->history_data + old_history->current_index); -+ } -+ -+ /* -+ * free new history (which was just temporary) -+ * note that we don't need to free history_data from new_history -+ * we freed the history_data from old history, and set that to the history_data from new_history -+ * so, this cleanup has already been handled -+ */ -+ kfree(new_history); -+ -+ } -+} -+static void adjust_id_for_backwards_time_shift(char* key, void* value) -+{ -+ info_and_maps* iam = (info_and_maps*)value; -+ if(iam == NULL) -+ { -+ return; -+ } -+ if(iam->info == NULL) -+ { -+ return; -+ } -+ -+ backwards_adjust_iam = iam; -+ if( (iam->info->reset_is_constant_interval == 0 && iam->info->reset_interval == BANDWIDTH_NEVER) || iam->info->cmp == BANDWIDTH_CHECK ) -+ { -+ return; -+ } -+ if(iam->ip_history_map != NULL) -+ { -+ backwards_adjust_info_previous_reset = iam->info->previous_reset; -+ backwards_adjust_ips_zeroed = 0; -+ apply_to_every_long_map_value(iam->ip_history_map, adjust_ip_for_backwards_time_shift); -+ } -+ else -+ { -+ time_t next_reset_after_adjustment = get_next_reset_time(iam->info, backwards_adjust_current_time, backwards_adjust_current_time); -+ if(next_reset_after_adjustment < iam->info->next_reset) -+ { -+ iam->info->previous_reset = backwards_adjust_current_time; -+ iam->info->next_reset = next_reset_after_adjustment; -+ } -+ } -+ backwards_adjust_iam = NULL; -+} -+static void check_for_backwards_time_shift(time_t now) -+{ -+ spin_lock_bh(&bandwidth_lock); -+ if(now < backwards_check && backwards_check != 0) -+ { -+ printk("ipt_bandwidth: backwards time shift detected, adjusting\n"); -+ -+ /* adjust */ -+ down(&userspace_lock); -+ -+ /* This function is always called with absolute time, not time adjusted for timezone. Correct that before adjusting. */ -+ backwards_adjust_current_time = now - local_seconds_west; -+ apply_to_every_string_map_value(id_map, adjust_id_for_backwards_time_shift); -+ up(&userspace_lock); -+ } -+ backwards_check = now; -+ spin_unlock_bh(&bandwidth_lock); -+} -+ -+ -+ -+static int old_minutes_west; -+static time_t shift_timezone_current_time; -+static time_t shift_timezone_info_previous_reset; -+static info_and_maps* shift_timezone_iam = NULL; -+static void shift_timezone_of_ip(unsigned long key, void* value) -+{ -+ #ifdef BANDWIDTH_DEBUG -+ unsigned long* ip = &key; -+ printk("shifting ip = %u.%u.%u.%u\n", *((char*)ip), *(((char*)ip)+1), *(((char*)ip)+2), *(((char*)ip)+3) ); -+ #endif -+ -+ -+ bw_history* history = (bw_history*)value; -+ int32_t timezone_adj = (old_minutes_west-local_minutes_west)*60; -+ #ifdef BANDWIDTH_DEBUG -+ printk(" before jump:\n"); -+ printk(" current time = %ld\n", shift_timezone_current_time); -+ printk(" first_start = %ld\n", history->first_start); -+ printk(" first_end = %ld\n", history->first_end); -+ printk(" last_end = %ld\n", history->last_end); -+ printk("\n"); -+ #endif -+ -+ /* given time after shift, calculate next and previous reset times */ -+ time_t next_reset = get_next_reset_time(shift_timezone_iam->info, shift_timezone_current_time, 0); -+ time_t previous_reset = get_nominal_previous_reset_time(shift_timezone_iam->info, next_reset); -+ shift_timezone_iam->info->next_reset = next_reset; -+ -+ /*if we're resetting on a constant interval, we can just adjust -- no need to worry about relationship to constant boundaries, e.g. end of day */ -+ if(shift_timezone_iam->info->reset_is_constant_interval) -+ { -+ shift_timezone_iam->info->previous_reset = previous_reset; -+ if(history->num_nodes > 1) -+ { -+ history->first_start = history->first_start + timezone_adj; -+ history->first_end = history->first_end + timezone_adj; -+ history->last_end = history->last_end + timezone_adj; -+ } -+ } -+ else -+ { -+ -+ -+ /* next reset will be the newly computed next_reset. */ -+ int node_index=history->num_nodes - 1; -+ if(node_index > 0) -+ { -+ /* based on new, shifted time, iterate back over all nodes in history */ -+ shift_timezone_iam->info->previous_reset = previous_reset ; -+ history->last_end = previous_reset; -+ -+ while(node_index > 1) -+ { -+ previous_reset = get_nominal_previous_reset_time(shift_timezone_iam->info, previous_reset); -+ node_index--; -+ } -+ history->first_end = previous_reset; -+ -+ previous_reset = get_nominal_previous_reset_time(shift_timezone_iam->info, previous_reset); -+ history->first_start = previous_reset > history->first_start + timezone_adj ? previous_reset : history->first_start + timezone_adj; -+ } -+ else -+ { -+ /* -+ * history hasn't really been initialized -- there's only one, current time point. -+ * we only know what's in the current accumulator in info. Just adjust previous reset time and make sure it's valid -+ */ -+ shift_timezone_iam->info->previous_reset = previous_reset > shift_timezone_info_previous_reset + timezone_adj ? previous_reset : shift_timezone_info_previous_reset + timezone_adj; -+ } -+ } -+ -+ -+ #ifdef BANDWIDTH_DEBUG -+ printk("\n"); -+ printk(" after jump:\n"); -+ printk(" first_start = %ld\n", history->first_start); -+ printk(" first_end = %ld\n", history->first_end); -+ printk(" last_end = %ld\n", history->last_end); -+ printk("\n\n"); -+ #endif -+ -+} -+static void shift_timezone_of_id(char* key, void* value) -+{ -+ info_and_maps* iam = (info_and_maps*)value; -+ int history_found = 0; -+ if(iam == NULL) -+ { -+ return; -+ } -+ if(iam->info == NULL) -+ { -+ return; -+ } -+ -+ #ifdef BANDWIDTH_DEBUG -+ printk("shifting id %s\n", key); -+ #endif -+ -+ shift_timezone_iam = iam; -+ if( (iam->info->reset_is_constant_interval == 0 && iam->info->reset_interval == BANDWIDTH_NEVER) || iam->info->cmp == BANDWIDTH_CHECK ) -+ { -+ return; -+ } -+ -+ if(iam->ip_history_map != NULL) -+ { -+ if(iam->ip_history_map->num_elements > 0) -+ { -+ history_found = 1; -+ shift_timezone_info_previous_reset = iam->info->previous_reset; -+ apply_to_every_long_map_value(iam->ip_history_map, shift_timezone_of_ip); -+ } -+ } -+ if(history_found == 0) -+ { -+ iam->info->previous_reset = iam->info->previous_reset + ((old_minutes_west - local_minutes_west )*60); -+ if(iam->info->previous_reset > shift_timezone_current_time) -+ { -+ iam->info->next_reset = get_next_reset_time(iam->info, shift_timezone_current_time, shift_timezone_current_time); -+ iam->info->previous_reset = shift_timezone_current_time; -+ } -+ else -+ { -+ iam->info->next_reset = get_next_reset_time(iam->info, shift_timezone_current_time, iam->info->previous_reset); -+ while (iam->info->next_reset < shift_timezone_current_time) -+ { -+ iam->info->previous_reset = iam->info->next_reset; -+ iam->info->next_reset = get_next_reset_time(iam->info, iam->info->previous_reset, iam->info->previous_reset); -+ } -+ } -+ } -+ shift_timezone_iam = NULL; -+} -+ -+static void check_for_timezone_shift(time_t now, int already_locked) -+{ -+ -+ if(already_locked == 0) { spin_lock_bh(&bandwidth_lock); } -+ if(now != last_local_mw_update ) /* make sure nothing changed while waiting for lock */ -+ { -+ local_minutes_west = sys_tz.tz_minuteswest; -+ local_seconds_west = 60*local_minutes_west; -+ last_local_mw_update = now; -+ if(local_seconds_west > last_local_mw_update) -+ { -+ /* we can't let adjusted time be < 0 -- pretend timezone is still UTC */ -+ local_minutes_west = 0; -+ local_seconds_west = 0; -+ } -+ -+ if(local_minutes_west != old_minutes_west) -+ { -+ int adj_minutes = old_minutes_west-local_minutes_west; -+ adj_minutes = adj_minutes < 0 ? adj_minutes*-1 : adj_minutes; -+ -+ if(already_locked == 0) { down(&userspace_lock); } -+ -+ printk("ipt_bandwidth: timezone shift of %d minutes detected, adjusting\n", adj_minutes); -+ printk(" old minutes west=%d, new minutes west=%d\n", old_minutes_west, local_minutes_west); -+ -+ /* this function is always called with absolute time, not time adjusted for timezone. Correct that before adjusting */ -+ shift_timezone_current_time = now - local_seconds_west; -+ apply_to_every_string_map_value(id_map, shift_timezone_of_id); -+ -+ old_minutes_west = local_minutes_west; -+ -+ -+ if(already_locked == 0) { up(&userspace_lock); } -+ } -+ } -+ if(already_locked == 0) { spin_unlock_bh(&bandwidth_lock); } -+} -+ -+ -+ -+static bw_history* initialize_history(uint32_t max_nodes) -+{ -+ bw_history* new_history = (bw_history*)kmalloc(sizeof(bw_history), GFP_ATOMIC); -+ if(new_history != NULL) -+ { -+ new_history->history_data = (uint64_t*)kmalloc((1+max_nodes)*sizeof(uint64_t), GFP_ATOMIC); /*number to save +1 for current */ -+ if(new_history->history_data == NULL) /* deal with malloc failure */ -+ { -+ kfree(new_history); -+ new_history = NULL; -+ } -+ else -+ { -+ new_history->first_start = 0; -+ new_history->first_end = 0; -+ new_history->last_end = 0; -+ new_history->max_nodes = max_nodes+1; /*number to save +1 for current */ -+ new_history->num_nodes = 1; -+ new_history->non_zero_nodes = 0; /* counts non_zero nodes other than current, so initialize to 0 */ -+ new_history->current_index = 0; -+ memset(new_history->history_data, 0, max_nodes*sizeof(uint64_t)); -+ } -+ } -+ return new_history; /* in case of malloc failure new_history will be NULL, this should be safe */ -+} -+ -+/* returns 1 if there are non-zero nodes in history, 0 if history is empty (all zero) */ -+static unsigned char update_history(bw_history* history, time_t interval_start, time_t interval_end, struct ipt_bandwidth_info* info) -+{ -+ unsigned char history_is_nonzero = 0; -+ if(history != NULL) /* should never be null, but let's be sure */ -+ { -+ -+ /* adjust number of non-zero nodes */ -+ if(history->num_nodes == history->max_nodes) -+ { -+ uint32_t first_index = (history->current_index+1) % history->max_nodes; -+ if( (history->history_data)[first_index] > 0) -+ { -+ history->non_zero_nodes = history->non_zero_nodes -1; -+ } -+ } -+ if( (history->history_data)[history->current_index] > 0 ) -+ { -+ history->non_zero_nodes = history->non_zero_nodes + 1; -+ } -+ history_is_nonzero = history->non_zero_nodes > 0 ? 1 : 0; -+ -+ -+ /* update interval start/end */ -+ if(history->first_start == 0) -+ { -+ history->first_start = interval_start; -+ history->first_end = interval_end; -+ } -+ if(history->num_nodes >= history->max_nodes) -+ { -+ history->first_start = history->first_end; -+ history->first_end = get_next_reset_time(info, history->first_start, history->first_start); -+ } -+ history->last_end = interval_end; -+ -+ -+ history->num_nodes = history->num_nodes < history->max_nodes ? history->num_nodes+1 : history->max_nodes; -+ history->current_index = (history->current_index+1) % history->max_nodes; -+ (history->history_data)[history->current_index] = 0; -+ -+ #ifdef BANDWIDTH_DEBUG -+ printk("after update history->num_nodes = %d\n", history->num_nodes); -+ printk("after update history->current_index = %d\n", history->current_index); -+ #endif -+ } -+ return history_is_nonzero; -+} -+ -+ -+static struct ipt_bandwidth_info* do_reset_info = NULL; -+static long_map* do_reset_ip_map = NULL; -+static long_map* do_reset_delete_ips = NULL; -+static time_t do_reset_interval_start = 0; -+static time_t do_reset_interval_end = 0; -+static void do_reset(unsigned long key, void* value) -+{ -+ bw_history* history = (bw_history*)value; -+ if(history != NULL && do_reset_info != NULL) /* should never be null.. but let's be sure */ -+ { -+ unsigned char history_contains_data = update_history(history, do_reset_interval_start, do_reset_interval_end, do_reset_info); -+ if(history_contains_data == 0 || do_reset_ip_map == NULL) -+ { -+ //schedule data for ip to be deleted (can't delete history while we're traversing history tree data structure!) -+ if(do_reset_delete_ips != NULL) /* should never be null.. but let's be sure */ -+ { -+ set_long_map_element(do_reset_delete_ips, key, (void*)(history->history_data + history->current_index)); -+ } -+ } -+ else -+ { -+ set_long_map_element(do_reset_ip_map, key, (void*)(history->history_data + history->current_index) ); -+ } -+ } -+} -+ -+long_map* clear_ip_map = NULL; -+long_map* clear_ip_history_map = NULL; -+static void clear_ips(unsigned long key, void* value) -+{ -+ if(clear_ip_history_map != NULL && clear_ip_map != NULL) -+ { -+ bw_history* history; -+ -+ #ifdef BANDWIDTH_DEBUG -+ unsigned long* ip = &key; -+ printk("clearing ip = %u.%u.%u.%u\n", *((char*)ip), *(((char*)ip)+1), *(((char*)ip)+2), *(((char*)ip)+3) ); -+ #endif -+ -+ remove_long_map_element(clear_ip_map, key); -+ history = (bw_history*)remove_long_map_element(clear_ip_history_map, key); -+ if(history != NULL) -+ { -+ kfree(history->history_data); -+ kfree(history); -+ } -+ } -+} -+ -+static void set_bandwidth_to_zero(unsigned long key, void* value) -+{ -+ *((uint64_t*)value) = 0; -+} -+ -+ -+long_map* reset_histories_ip_map = NULL; -+static void reset_histories(unsigned long key, void* value) -+{ -+ bw_history* bh = (bw_history*)value; -+ bh->first_start = 0; -+ bh->first_end = 0; -+ bh->last_end = 0; -+ bh->num_nodes = 1; -+ bh->non_zero_nodes = 1; -+ bh->current_index = 0; -+ (bh->history_data)[0] = 0; -+ if(reset_histories_ip_map != NULL) -+ { -+ set_long_map_element(reset_histories_ip_map, key, bh->history_data); -+ } -+} -+ -+ -+static void handle_interval_reset(info_and_maps* iam, time_t now) -+{ -+ struct ipt_bandwidth_info* info; -+ -+ #ifdef BANDWIDTH_DEBUG -+ printk("now, handling interval reset\n"); -+ #endif -+ if(iam == NULL) -+ { -+ #ifdef BANDWIDTH_DEBUG -+ printk("error: doing reset, iam is null \n"); -+ #endif -+ return; -+ } -+ if(iam->ip_map == NULL) -+ { -+ #ifdef BANDWIDTH_DEBUG -+ printk("error: doing reset, ip_map is null\n"); -+ #endif -+ return; -+ } -+ if(iam->info == NULL) -+ { -+ #ifdef BANDWIDTH_DEBUG -+ printk("error: doing reset, info is null\n"); -+ #endif -+ -+ return; -+ } -+ -+ info = iam->info; -+ if(info->num_intervals_to_save == 0) -+ { -+ #ifdef BANDWIDTH_DEBUG -+ printk("doing reset for case where no intervals are saved\n"); -+ #endif -+ -+ if(info->next_reset <= now) -+ { -+ info->next_reset = get_next_reset_time(info, info->previous_reset, info->previous_reset); -+ if(info->next_reset <= now) -+ { -+ info->next_reset = get_next_reset_time(info, now, info->previous_reset); -+ } -+ } -+ apply_to_every_long_map_value(iam->ip_map, set_bandwidth_to_zero); -+ } -+ else -+ { -+ unsigned long num_updates; -+ #ifdef BANDWIDTH_DEBUG -+ printk("doing reset for case where at least one interval is saved\n"); -+ #endif -+ -+ -+ if(iam->ip_history_map == NULL) -+ { -+ #ifdef BANDWIDTH_DEBUG -+ printk("error: doing reset, history_map is null when num_intervals_to_save > 0\n"); -+ #endif -+ return; -+ } -+ -+ do_reset_info = info; -+ do_reset_ip_map = iam->ip_map; -+ clear_ip_map = iam->ip_map; -+ clear_ip_history_map = iam->ip_history_map; -+ -+ -+ /* -+ * at most update as many times as we have intervals to save -- prevents -+ * rediculously long loop if interval length is 2 seconds and time was -+ * reset to 5 years in the future -+ */ -+ num_updates = 0; -+ while(info->next_reset <= now && num_updates < info->num_intervals_to_save) -+ { -+ do_reset_delete_ips = initialize_long_map(); -+ /* -+ * don't check for malloc failure here -- we -+ * include tests for whether do_reset_delete_ips -+ * is null below (reset should still be able to procede) -+ */ -+ -+ do_reset_interval_start = info->previous_reset; -+ do_reset_interval_end = info->next_reset; -+ -+ apply_to_every_long_map_value(iam->ip_history_map, do_reset); -+ -+ -+ info->previous_reset = info->next_reset; -+ info->next_reset = get_next_reset_time(info, info->previous_reset, info->previous_reset); -+ -+ /* free all data for ips whose entire histories contain only zeros to conserve space */ -+ if(do_reset_delete_ips != NULL) -+ { -+ unsigned long num_destroyed; -+ -+ /* only clear ips if this is the last iteration of this update */ -+ if(info->next_reset >= now) -+ { -+ /* -+ * no need to reset iam->info->combined_bw if it gets deleted here. -+ * below, at end of function it will get set to NULL if it gets wiped -+ */ -+ -+ apply_to_every_long_map_value(do_reset_delete_ips, clear_ips); -+ } -+ -+ /* but clear do_reset_delete_ips no matter what, values are just pointers to history data so we can ignore them */ -+ destroy_long_map(do_reset_delete_ips, DESTROY_MODE_IGNORE_VALUES, &num_destroyed); -+ do_reset_delete_ips = NULL; -+ } -+ num_updates++; -+ } -+ do_reset_info = NULL; -+ do_reset_ip_map = NULL; -+ clear_ip_map = NULL; -+ clear_ip_history_map = NULL; -+ -+ do_reset_interval_start = 0; -+ do_reset_interval_end = 0; -+ -+ /* -+ * test if we've cycled past all existing data -- if so wipe all existing histories -+ * and set previous reset time to now, and compute next reset time from -+ * current time -+ */ -+ if(info->next_reset <= now) -+ { -+ reset_histories_ip_map = iam->ip_map; -+ apply_to_every_long_map_value(iam->ip_history_map, reset_histories); -+ reset_histories_ip_map = NULL; -+ -+ info->previous_reset = now; -+ info->next_reset = get_next_reset_time(info, now, info->previous_reset); -+ } -+ } -+ info->combined_bw = (uint64_t*)get_long_map_element(iam->ip_map, 0); -+ info->current_bandwidth = 0; -+} -+ -+/* -+ * set max bandwidth to be max possible using 63 of the -+ * 64 bits in our record. In some systems uint64_t is treated -+ * like signed, so to prevent errors, use only 63 bits -+ */ -+static uint64_t pow64(uint64_t base, uint64_t pow) -+{ -+ uint64_t val = 1; -+ if(pow > 0) -+ { -+ val = base*pow64(base, pow-1); -+ } -+ return val; -+} -+static uint64_t get_bw_record_max(void) /* called by init to set global variable */ -+{ -+ return (pow64(2,62)) + (pow64(2,62)-1); -+} -+static uint64_t bandwidth_record_max; -+ -+ -+#define ADD_UP_TO_MAX(original,add,is_check) (bandwidth_record_max - original > add && is_check== 0) ? original+add : (is_check ? original : bandwidth_record_max); -+ -+ -+/* -+ * Shamelessly yoinked from xt_time.c -+ * "That is so amazingly amazing, I think I'd like to steal it." -+ * -- Zaphod Beeblebrox -+ */ -+ -+static const u_int16_t days_since_year[] = { -+ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, -+}; -+ -+static const u_int16_t days_since_leapyear[] = { -+ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, -+}; -+ -+/* -+ * Since time progresses forward, it is best to organize this array in reverse, -+ * to minimize lookup time. These are days since epoch since start of each year, -+ * going back to 1970 -+ */ -+#define DSE_FIRST 2039 -+static const u_int16_t days_since_epoch_for_each_year_start[] = { -+ /* 2039 - 2030 */ -+ 25202, 24837, 24472, 24106, 23741, 23376, 23011, 22645, 22280, 21915, -+ /* 2029 - 2020 */ -+ 21550, 21184, 20819, 20454, 20089, 19723, 19358, 18993, 18628, 18262, -+ /* 2019 - 2010 */ -+ 17897, 17532, 17167, 16801, 16436, 16071, 15706, 15340, 14975, 14610, -+ /* 2009 - 2000 */ -+ 14245, 13879, 13514, 13149, 12784, 12418, 12053, 11688, 11323, 10957, -+ /* 1999 - 1990 */ -+ 10592, 10227, 9862, 9496, 9131, 8766, 8401, 8035, 7670, 7305, -+ /* 1989 - 1980 */ -+ 6940, 6574, 6209, 5844, 5479, 5113, 4748, 4383, 4018, 3652, -+ /* 1979 - 1970 */ -+ 3287, 2922, 2557, 2191, 1826, 1461, 1096, 730, 365, 0, -+}; -+ -+static inline int is_leap(unsigned int y) -+{ -+ return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0); -+} -+ -+/* end of code yoinked from xt_time */ -+ -+ -+static time_t get_nominal_previous_reset_time(struct ipt_bandwidth_info *info, time_t current_next_reset) -+{ -+ time_t previous_reset = current_next_reset; -+ if(info->reset_is_constant_interval == 0) -+ { -+ /* skip backwards in halves of interval after next, until */ -+ time_t next = get_next_reset_time(info, current_next_reset, 0); -+ time_t half_interval = (next-current_next_reset)/2; -+ time_t half_count, tmp; -+ half_interval = half_interval == 0 ? 1 : half_interval; /* must be at least one second, otherwise we loop forever*/ -+ -+ half_count = 1; -+ tmp = get_next_reset_time(info, (current_next_reset-(half_count*half_interval)),0); -+ while(previous_reset >= current_next_reset) -+ { -+ previous_reset = tmp; -+ half_count++; -+ tmp = get_next_reset_time(info, (current_next_reset-(half_count*half_interval)),0); -+ } -+ } -+ else -+ { -+ previous_reset = current_next_reset - info->reset_interval; -+ } -+ return previous_reset; -+} -+ -+ -+static time_t get_next_reset_time(struct ipt_bandwidth_info *info, time_t now, time_t previous_reset) -+{ -+ //first calculate when next reset would be if reset_time is 0 (which it may be) -+ time_t next_reset = 0; -+ if(info->reset_is_constant_interval == 0) -+ { -+ if(info->reset_interval == BANDWIDTH_MINUTE) -+ { -+ next_reset = ( (long)(now/60) + 1)*60; -+ if(info->reset_time > 0) -+ { -+ time_t alt_reset = next_reset + info->reset_time - 60; -+ next_reset = alt_reset > now ? alt_reset : next_reset+info->reset_time; -+ } -+ } -+ else if(info->reset_interval == BANDWIDTH_HOUR) -+ { -+ next_reset = ( (long)(now/(60*60)) + 1)*60*60; -+ if(info->reset_time > 0) -+ { -+ time_t alt_reset = next_reset + info->reset_time - (60*60); -+ next_reset = alt_reset > now ? alt_reset : next_reset+info->reset_time; -+ } -+ } -+ else if(info->reset_interval == BANDWIDTH_DAY) -+ { -+ next_reset = ( (long)(now/(60*60*24)) + 1)*60*60*24; -+ if(info->reset_time > 0) -+ { -+ time_t alt_reset = next_reset + info->reset_time - (60*60*24); -+ next_reset = alt_reset > now ? alt_reset : next_reset+info->reset_time; -+ } -+ } -+ else if(info->reset_interval == BANDWIDTH_WEEK) -+ { -+ long days_since_epoch = now/(60*60*24); -+ long current_weekday = (4 + days_since_epoch ) % 7 ; -+ next_reset = (days_since_epoch + (7-current_weekday) )*(60*60*24); -+ if(info->reset_time > 0) -+ { -+ time_t alt_reset = next_reset + info->reset_time - (60*60*24*7); -+ next_reset = alt_reset > now ? alt_reset : next_reset+info->reset_time; -+ } -+ } -+ else if(info->reset_interval == BANDWIDTH_MONTH) -+ { -+ /* yeah, most of this is yoinked from xt_time too */ -+ int year; -+ int year_index; -+ int year_day; -+ int month; -+ long days_since_epoch = now/(60*60*24); -+ uint16_t* month_start_days; -+ time_t alt_reset; -+ -+ for (year_index = 0, year = DSE_FIRST; days_since_epoch_for_each_year_start[year_index] > days_since_epoch; year_index++) -+ { -+ year--; -+ } -+ year_day = days_since_epoch - days_since_epoch_for_each_year_start[year_index]; -+ if (is_leap(year)) -+ { -+ month_start_days = (u_int16_t*)days_since_leapyear; -+ } -+ else -+ { -+ month_start_days = (u_int16_t*)days_since_year; -+ } -+ for (month = 11 ; month > 0 && month_start_days[month] > year_day; month--){} -+ -+ /* end majority of yoinkage */ -+ -+ alt_reset = (days_since_epoch_for_each_year_start[year_index] + month_start_days[month])*(60*60*24) + info->reset_time; -+ if(alt_reset > now) -+ { -+ next_reset = alt_reset; -+ } -+ else if(month == 11) -+ { -+ next_reset = days_since_epoch_for_each_year_start[year_index-1]*(60*60*24) + info->reset_time; -+ } -+ else -+ { -+ next_reset = (days_since_epoch_for_each_year_start[year_index] + month_start_days[month+1])*(60*60*24) + info->reset_time; -+ } -+ } -+ } -+ else -+ { -+ if(info->reset_time > 0 && previous_reset > 0 && previous_reset <= now) -+ { -+ unsigned long adj_reset_time = info->reset_time; -+ unsigned long tz_secs = 60 * local_minutes_west; -+ if(adj_reset_time < tz_secs) -+ { -+ unsigned long interval_multiple = 1+(tz_secs/info->reset_interval); -+ adj_reset_time = adj_reset_time + (interval_multiple*info->reset_interval); -+ } -+ adj_reset_time = adj_reset_time - tz_secs; -+ -+ if(info->reset_time > now) -+ { -+ unsigned long whole_intervals = ((info->reset_time - now)/info->reset_interval) + 1; /* add one to make sure integer gets rounded UP (since we're subtracting) */ -+ next_reset = info->reset_time - (whole_intervals*info->reset_interval); -+ while(next_reset <= now) -+ { -+ next_reset = next_reset + info->reset_interval; -+ } -+ -+ } -+ else /* info->reset_time <= now */ -+ { -+ unsigned long whole_intervals = (now-info->reset_time)/info->reset_interval; /* integer gets rounded down */ -+ next_reset = info->reset_time + (whole_intervals*info->reset_interval); -+ while(next_reset <= now) -+ { -+ next_reset = next_reset + info->reset_interval; -+ } -+ } -+ } -+ else if(previous_reset > 0) -+ { -+ next_reset = previous_reset; -+ if(next_reset <= now) /* check just to be sure, if this is not true VERY BAD THINGS will happen */ -+ { -+ unsigned long whole_intervals = (now-next_reset)/info->reset_interval; /* integer gets rounded down */ -+ next_reset = next_reset + (whole_intervals*info->reset_interval); -+ while(next_reset <= now) -+ { -+ next_reset = next_reset + info->reset_interval; -+ } -+ } -+ } -+ else -+ { -+ next_reset = now + info->reset_interval; -+ } -+ } -+ -+ return next_reset; -+} -+ -+ -+ -+static uint64_t* initialize_map_entries_for_ip(info_and_maps* iam, unsigned long ip, uint64_t initial_bandwidth) -+{ -+ #ifdef BANDWIDTH_DEBUG -+ printk("initializing entry for ip, bw=%lld\n", initial_bandwidth); -+ #endif -+ -+ #ifdef BANDWIDTH_DEBUG -+ if(iam == NULL){ printk("error in initialization: iam is null!\n"); } -+ #endif -+ -+ -+ uint64_t* new_bw = NULL; -+ if(iam != NULL) /* should never happen, but let's be certain */ -+ { -+ struct ipt_bandwidth_info *info = iam->info; -+ long_map* ip_map = iam->ip_map; -+ long_map* ip_history_map = iam->ip_history_map; -+ -+ #ifdef BANDWIDTH_DEBUG -+ if(info == NULL){ printk("error in initialization: info is null!\n"); } -+ if(ip_map == NULL){ printk("error in initialization: ip_map is null!\n"); } -+ #endif -+ -+ -+ if(info != NULL && ip_map != NULL) /* again... should never happen but let's be sure */ -+ { -+ if(info->num_intervals_to_save == 0 || ip_history_map == NULL) -+ { -+ #ifdef BANDWIDTH_DEBUG -+ printk(" initializing entry for ip without history\n"); -+ #endif -+ new_bw = (uint64_t*)kmalloc(sizeof(uint64_t), GFP_ATOMIC); -+ } -+ else -+ { -+ #ifdef BANDWIDTH_DEBUG -+ printk(" initializing entry for ip with history\n"); -+ #endif -+ -+ bw_history *new_history = initialize_history(info->num_intervals_to_save); -+ if(new_history != NULL) /* check for kmalloc failure */ -+ { -+ bw_history* old_history; -+ #ifdef BANDWIDTH_DEBUG -+ printk(" malloc succeeded, new history is non-null\n"); -+ #endif -+ -+ new_bw = (uint64_t*)(new_history->history_data + new_history->current_index); -+ old_history = set_long_map_element(ip_history_map, ip, (void*)new_history); -+ if(old_history != NULL) -+ { -+ #ifdef BANDWIDTH_DEBUG -+ printk(" after initialization old_history not null! (something is FUBAR)\n"); -+ #endif -+ kfree(old_history->history_data); -+ kfree(old_history); -+ } -+ -+ #ifdef BANDWIDTH_DEBUG -+ -+ #endif -+ } -+ } -+ if(new_bw != NULL) /* check for kmalloc failure */ -+ { -+ uint64_t* old_bw; -+ *new_bw = initial_bandwidth; -+ old_bw = set_long_map_element(ip_map, ip, (void*)new_bw ); -+ -+ /* only free old_bw if num_intervals_to_save is zero -- otherwise it already got freed above when we wiped the old history */ -+ if(old_bw != NULL && info->num_intervals_to_save == 0) -+ { -+ free(old_bw); -+ } -+ -+ if(ip == 0) -+ { -+ info->combined_bw = new_bw; -+ } -+ -+ #ifdef BANDWIDTH_DEBUG -+ uint64_t *test = (uint64_t*)get_long_map_element(ip_map, ip); -+ if(test == NULL) -+ { -+ printk(" after initialization bw is null!\n"); -+ } -+ else -+ { -+ printk(" after initialization bw is %lld\n", *new_bw); -+ printk(" after initialization test is %lld\n", *test); -+ } -+ #endif -+ } -+ } -+ } -+ -+ return new_bw; -+} -+ -+ -+static bool match(const struct sk_buff *skb, struct xt_action_param *par) -+{ -+ -+ struct ipt_bandwidth_info *info = ((const struct ipt_bandwidth_info*)(par->matchinfo))->non_const_self; -+ -+ time_t now; -+ int match_found; -+ -+ -+ unsigned char is_check = info->cmp == BANDWIDTH_CHECK ? 1 : 0; -+ unsigned char do_src_dst_swap = 0; -+ info_and_maps* iam = NULL; -+ long_map* ip_map = NULL; -+ -+ uint64_t* bws[2] = {NULL, NULL}; -+ -+ /* if we're currently setting this id, ignore new data until set is complete */ -+ if(set_in_progress == 1) -+ { -+ if(strcmp(info->id, set_id) == 0) -+ { -+ return 0; -+ } -+ } -+ -+ -+ -+ -+ /* -+ * BEFORE we lock, check for timezone shift -+ * this will almost always be be very,very quick, -+ * but in the event there IS a shift this -+ * function will lock both kernel update spinlock -+ * and userspace i/o semaphore, and do a lot of -+ * number crunching so we shouldn't -+ * already be locked. -+ */ -+ now = get_seconds(); -+ -+ -+ if(now != last_local_mw_update ) -+ { -+ check_for_timezone_shift(now, 0); -+ check_for_backwards_time_shift(now); -+ } -+ now = now - local_seconds_west; /* Adjust for local timezone */ -+ -+ spin_lock_bh(&bandwidth_lock); -+ -+ if(is_check) -+ { -+ info_and_maps* check_iam; -+ do_src_dst_swap = info->check_type == BANDWIDTH_CHECK_SWAP ? 1 : 0; -+ check_iam = (info_and_maps*)get_string_map_element_with_hashed_key(id_map, info->hashed_id); -+ if(check_iam == NULL) -+ { -+ spin_unlock_bh(&bandwidth_lock); -+ return 0; -+ } -+ info = check_iam->info; -+ } -+ -+ -+ -+ -+ if(info->reset_interval != BANDWIDTH_NEVER) -+ { -+ if(info->next_reset < now) -+ { -+ //do reset -+ //iam = (info_and_maps*)get_string_map_element_with_hashed_key(id_map, info->hashed_id); -+ iam = (info_and_maps*)info->iam; -+ if(iam != NULL) /* should never be null, but let's be sure */ -+ { -+ handle_interval_reset(iam, now); -+ ip_map = iam->ip_map; -+ } -+ else -+ { -+ /* even in case of malloc failure or weird error we can update these params */ -+ info->current_bandwidth = 0; -+ info->next_reset = get_next_reset_time(info, now, info->previous_reset); -+ } -+ } -+ } -+ -+ if(info->type == BANDWIDTH_COMBINED) -+ { -+ if(iam == NULL) -+ { -+ //iam = (info_and_maps*)get_string_map_element_with_hashed_key(id_map, info->hashed_id); -+ iam = (info_and_maps*)info->iam; -+ if(iam != NULL) -+ { -+ ip_map = iam->ip_map; -+ } -+ } -+ if(ip_map != NULL) /* if this ip_map != NULL iam can never be NULL, so we don't need to check this */ -+ { -+ -+ if(info->combined_bw == NULL) -+ { -+ bws[0] = initialize_map_entries_for_ip(iam, 0, skb->len); -+ } -+ else -+ { -+ bws[0] = info->combined_bw; -+ *(bws[0]) = ADD_UP_TO_MAX(*(bws[0]), (uint64_t)skb->len, is_check); -+ } -+ } -+ else -+ { -+ #ifdef BANDWIDTH_DEBUG -+ printk("error: ip_map is null in match!\n"); -+ #endif -+ } -+ info->current_bandwidth = ADD_UP_TO_MAX(info->current_bandwidth, (uint64_t)skb->len, is_check); -+ } -+ else -+ { -+ uint32_t bw_ip, bw_ip_index; -+ uint32_t bw_ips[2] = {0, 0}; -+ struct iphdr* iph = (struct iphdr*)(skb_network_header(skb)); -+ if(info->type == BANDWIDTH_INDIVIDUAL_SRC) -+ { -+ //src ip -+ bw_ips[0] = iph->saddr; -+ if(do_src_dst_swap) -+ { -+ bw_ips[0] = iph->daddr; -+ } -+ } -+ else if (info->type == BANDWIDTH_INDIVIDUAL_DST) -+ { -+ //dst ip -+ bw_ips[0] = iph->daddr; -+ if(do_src_dst_swap) -+ { -+ bw_ips[0] = iph->saddr; -+ } -+ } -+ else if(info->type == BANDWIDTH_INDIVIDUAL_LOCAL || info->type == BANDWIDTH_INDIVIDUAL_REMOTE) -+ { -+ //remote or local ip -- need to test both src && dst -+ uint32_t src_ip = iph->saddr; -+ uint32_t dst_ip = iph->daddr; -+ if(info->type == BANDWIDTH_INDIVIDUAL_LOCAL) -+ { -+ bw_ips[0] = ((info->local_subnet_mask & src_ip) == info->local_subnet) ? src_ip : 0; -+ bw_ips[1] = ((info->local_subnet_mask & dst_ip) == info->local_subnet) ? dst_ip : 0; -+ } -+ else if(info->type == BANDWIDTH_INDIVIDUAL_REMOTE) -+ { -+ bw_ips[0] = ((info->local_subnet_mask & src_ip) != info->local_subnet ) ? src_ip : 0; -+ bw_ips[1] = ((info->local_subnet_mask & dst_ip) != info->local_subnet ) ? dst_ip : 0; -+ } -+ } -+ -+ if(ip_map == NULL) -+ { -+ //iam = (info_and_maps*)get_string_map_element_with_hashed_key(id_map, info->hashed_id); -+ iam = (info_and_maps*)info->iam; -+ if(iam != NULL) -+ { -+ ip_map = iam->ip_map; -+ } -+ } -+ if(!is_check && info->cmp == BANDWIDTH_MONITOR) -+ { -+ uint64_t* combined_oldval = info->combined_bw; -+ if(combined_oldval == NULL) -+ { -+ combined_oldval = initialize_map_entries_for_ip(iam, 0, (uint64_t)skb->len); -+ } -+ else -+ { -+ *combined_oldval = ADD_UP_TO_MAX(*combined_oldval, (uint64_t)skb->len, is_check); -+ } -+ } -+ bw_ip_index = bw_ips[0] == 0 ? 1 : 0; -+ bw_ip = bw_ips[bw_ip_index]; -+ if(bw_ip != 0 && ip_map != NULL) -+ { -+ uint64_t* oldval = get_long_map_element(ip_map, (unsigned long)bw_ip); -+ if(oldval == NULL) -+ { -+ if(!is_check) -+ { -+ /* may return NULL on malloc failure but that's ok */ -+ oldval = initialize_map_entries_for_ip(iam, (unsigned long)bw_ip, (uint64_t)skb->len); -+ } -+ } -+ else -+ { -+ *oldval = ADD_UP_TO_MAX(*oldval, (uint64_t)skb->len, is_check); -+ } -+ -+ /* this is fine, setting bws[bw_ip_index] to NULL on check for undefined value or kmalloc failure won't crash anything */ -+ bws[bw_ip_index] = oldval; -+ } -+ -+ } -+ -+ -+ match_found = 0; -+ if(info->cmp != BANDWIDTH_MONITOR) -+ { -+ if(info->cmp == BANDWIDTH_GT) -+ { -+ match_found = bws[0] != NULL ? ( *(bws[0]) > info->bandwidth_cutoff ? 1 : match_found ) : match_found; -+ match_found = bws[1] != NULL ? ( *(bws[1]) > info->bandwidth_cutoff ? 1 : match_found ) : match_found; -+ match_found = info->current_bandwidth > info->bandwidth_cutoff ? 1 : match_found; -+ } -+ else if(info->cmp == BANDWIDTH_LT) -+ { -+ match_found = bws[0] != NULL ? ( *(bws[0]) < info->bandwidth_cutoff ? 1 : match_found ) : match_found; -+ match_found = bws[1] != NULL ? ( *(bws[1]) < info->bandwidth_cutoff ? 1 : match_found ) : match_found; -+ match_found = info->current_bandwidth < info->bandwidth_cutoff ? 1 : match_found; -+ } -+ } -+ -+ -+ spin_unlock_bh(&bandwidth_lock); -+ -+ -+ -+ -+ -+ return match_found; -+} -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+/********************** -+ * Get functions -+ *********************/ -+ -+#define MAX_IP_STR_LENGTH 16 -+ -+#define ERROR_NONE 0 -+#define ERROR_NO_ID 1 -+#define ERROR_BUFFER_TOO_SHORT 2 -+#define ERROR_NO_HISTORY 3 -+#define ERROR_UNKNOWN 4 -+typedef struct get_req_struct -+{ -+ uint32_t ip; -+ uint32_t next_ip_index; -+ unsigned char return_history; -+ char id[BANDWIDTH_MAX_ID_LENGTH]; -+} get_request; -+ -+static unsigned long* output_ip_list = NULL; -+static unsigned long output_ip_list_length = 0; -+ -+static char add_ip_block( uint32_t ip, -+ unsigned char full_history_requested, -+ info_and_maps* iam, -+ unsigned char* output_buffer, -+ uint32_t* current_output_index, -+ uint32_t buffer_length -+ ); -+static void parse_get_request(unsigned char* request_buffer, get_request* parsed_request); -+static int handle_get_failure(int ret_value, int unlock_user_sem, int unlock_bandwidth_spin, unsigned char error_code, unsigned char* out_buffer, unsigned char* free_buffer ); -+ -+ -+/* -+ * returns whether we succeeded in adding ip block, 0= success, -+ * otherwise error code of problem that we found -+ */ -+static char add_ip_block( uint32_t ip, -+ unsigned char full_history_requested, -+ info_and_maps* iam, -+ unsigned char* output_buffer, -+ uint32_t* current_output_index, -+ uint32_t output_buffer_length -+ ) -+{ -+ #ifdef BANDWIDTH_DEBUG -+ uint32_t *ipp = &ip; -+ printk("doing output for ip = %u.%u.%u.%u\n", *((unsigned char*)ipp), *(((unsigned char*)ipp)+1), *(((unsigned char*)ipp)+2), *(((unsigned char*)ipp)+3) ); -+ #endif -+ -+ if(full_history_requested) -+ { -+ bw_history* history = NULL; -+ if(iam->info->num_intervals_to_save > 0 && iam->ip_history_map != NULL) -+ { -+ history = (bw_history*)get_long_map_element(iam->ip_history_map, ip); -+ } -+ if(history == NULL) -+ { -+ #ifdef BANDWIDTH_DEBUG -+ printk(" no history map for ip, dumping latest value in history format\n" ); -+ #endif -+ -+ -+ uint32_t block_length = (2*4) + (3*8); -+ uint64_t *bw; -+ -+ if(*current_output_index + block_length > output_buffer_length) -+ { -+ return ERROR_BUFFER_TOO_SHORT; -+ } -+ *( (uint32_t*)(output_buffer + *current_output_index) ) = ip; -+ *current_output_index = *current_output_index + 4; -+ -+ *( (uint32_t*)(output_buffer + *current_output_index) ) = 1; -+ *current_output_index = *current_output_index + 4; -+ -+ *( (uint64_t*)(output_buffer + *current_output_index) ) = (uint64_t)iam->info->previous_reset + (60 * local_minutes_west); -+ *current_output_index = *current_output_index + 8; -+ -+ *( (uint64_t*)(output_buffer + *current_output_index) ) = (uint64_t)iam->info->previous_reset + (60 * local_minutes_west); -+ *current_output_index = *current_output_index + 8; -+ -+ *( (uint64_t*)(output_buffer + *current_output_index) ) = (uint64_t)iam->info->previous_reset + (60 * local_minutes_west); -+ *current_output_index = *current_output_index + 8; -+ -+ bw = (uint64_t*)get_long_map_element(iam->ip_map, ip); -+ if(bw == NULL) -+ { -+ *( (uint64_t*)(output_buffer + *current_output_index) ) = 0; -+ } -+ else -+ { -+ *( (uint64_t*)(output_buffer + *current_output_index) ) = *bw; -+ } -+ *current_output_index = *current_output_index + 8; -+ -+ } -+ else -+ { -+ uint32_t block_length = (2*4) + (3*8) + (8*history->num_nodes); -+ uint64_t last_reset; -+ uint32_t node_num; -+ uint32_t next_index; -+ -+ if(*current_output_index + block_length > output_buffer_length) -+ { -+ return ERROR_BUFFER_TOO_SHORT; -+ } -+ -+ *( (uint32_t*)(output_buffer + *current_output_index) ) = ip; -+ *current_output_index = *current_output_index + 4; -+ -+ *( (uint32_t*)(output_buffer + *current_output_index) )= history->num_nodes; -+ *current_output_index = *current_output_index + 4; -+ -+ -+ -+ /* need to return times in regular UTC not the UTC - minutes west, which is useful for processing */ -+ last_reset = (uint64_t)iam->info->previous_reset + (60 * local_minutes_west); -+ *( (uint64_t*)(output_buffer + *current_output_index) ) = history->first_start > 0 ? (uint64_t)history->first_start + (60 * local_minutes_west) : last_reset; -+ #ifdef BANDWIDTH_DEBUG -+ printk(" dumping first start = %lld\n", *( (uint64_t*)(output_buffer + *current_output_index) ) ); -+ #endif -+ *current_output_index = *current_output_index + 8; -+ -+ -+ -+ *( (uint64_t*)(output_buffer + *current_output_index) ) = history->first_end > 0 ? (uint64_t)history->first_end + (60 * local_minutes_west) : last_reset; -+ #ifdef BANDWIDTH_DEBUG -+ printk(" dumping first end = %lld\n", *( (uint64_t*)(output_buffer + *current_output_index) ) ); -+ #endif -+ *current_output_index = *current_output_index + 8; -+ -+ -+ -+ *( (uint64_t*)(output_buffer + *current_output_index) ) = history->last_end > 0 ? (uint64_t)history->last_end + (60 * local_minutes_west) : last_reset; -+ #ifdef BANDWIDTH_DEBUG -+ printk(" dumping last end = %lld\n", *( (uint64_t*)(output_buffer + *current_output_index) ) ); -+ #endif -+ *current_output_index = *current_output_index + 8; -+ -+ -+ -+ node_num = 0; -+ next_index = history->num_nodes == history->max_nodes ? history->current_index+1 : 0; -+ next_index = next_index >= history->max_nodes ? 0 : next_index; -+ for(node_num=0; node_num < history->num_nodes; node_num++) -+ { -+ *( (uint64_t*)(output_buffer + *current_output_index) ) = (history->history_data)[ next_index ]; -+ *current_output_index = *current_output_index + 8; -+ next_index = (next_index + 1) % history->max_nodes; -+ } -+ } -+ } -+ else -+ { -+ uint64_t *bw; -+ if(*current_output_index + 8 > output_buffer_length) -+ { -+ return ERROR_BUFFER_TOO_SHORT; -+ } -+ -+ *( (uint32_t*)(output_buffer + *current_output_index) ) = ip; -+ *current_output_index = *current_output_index + 4; -+ -+ -+ bw = (uint64_t*)get_long_map_element(iam->ip_map, ip); -+ if(bw == NULL) -+ { -+ *( (uint64_t*)(output_buffer + *current_output_index) ) = 0; -+ } -+ else -+ { -+ *( (uint64_t*)(output_buffer + *current_output_index) ) = *bw; -+ } -+ *current_output_index = *current_output_index + 8; -+ } -+ return ERROR_NONE; -+} -+ -+ -+ -+/* -+ * convenience method for cleaning crap up after failed malloc or other -+ * error that we can't recover from in get function -+ */ -+static int handle_get_failure(int ret_value, int unlock_user_sem, int unlock_bandwidth_spin, unsigned char error_code, unsigned char* out_buffer, unsigned char* free_buffer ) -+{ -+ copy_to_user(out_buffer, &error_code, 1); -+ if( free_buffer != NULL ) { kfree(free_buffer); } -+ if(unlock_bandwidth_spin) { spin_unlock_bh(&bandwidth_lock); } -+ if(unlock_user_sem) { up(&userspace_lock); } -+ return ret_value; -+} -+ -+/* -+ * request structure: -+ * bytes 1:4 is ip (uint32_t) -+ * bytes 4:8 is the next ip index (uint32_t) -+ * byte 9 is whether to return full history or just current usage (unsigned char) -+ * bytes 10:10+MAX_ID_LENGTH are the id (a string) -+ */ -+static void parse_get_request(unsigned char* request_buffer, get_request* parsed_request) -+{ -+ uint32_t* ip = (uint32_t*)(request_buffer+0); -+ uint32_t* next_ip_index = (uint32_t*)(request_buffer+4); -+ unsigned char* return_history = (unsigned char*)(request_buffer+8); -+ -+ -+ -+ parsed_request->ip = *ip; -+ parsed_request->next_ip_index = *next_ip_index; -+ parsed_request->return_history = *return_history; -+ memcpy(parsed_request->id, request_buffer+9, BANDWIDTH_MAX_ID_LENGTH); -+ (parsed_request->id)[BANDWIDTH_MAX_ID_LENGTH-1] = '\0'; /* make sure id is null terminated no matter what */ -+ -+ #ifdef BANDWIDTH_DEBUG -+ printk("ip = %u.%u.%u.%u\n", *((char*)ip), *(((char*)ip)+1), *(((char*)ip)+2), *(((char*)ip)+3) ); -+ printk("next ip index = %d\n", *next_ip_index); -+ printk("return_history = %d\n", *return_history); -+ #endif -+} -+ -+ -+static int ipt_bandwidth_get_ctl(struct sock *sk, int cmd, void *user, int *len) -+{ -+ /* check for timezone shift & adjust if necessary */ -+ char* buffer; -+ get_request query; -+ info_and_maps* iam; -+ -+ unsigned char* error; -+ uint32_t* total_ips; -+ uint32_t* start_index; -+ uint32_t* num_ips_in_response; -+ uint64_t* reset_interval; -+ uint64_t* reset_time; -+ unsigned char* reset_is_constant_interval; -+ uint32_t current_output_index; -+ time_t now = get_seconds(); -+ check_for_timezone_shift(now, 0); -+ check_for_backwards_time_shift(now); -+ now = now - local_seconds_west; /* Adjust for local timezone */ -+ -+ -+ down(&userspace_lock); -+ -+ -+ /* first check that query buffer is big enough to hold the info needed to parse the query */ -+ if(*len < BANDWIDTH_MAX_ID_LENGTH + 9) -+ { -+ -+ return handle_get_failure(0, 1, 0, ERROR_BUFFER_TOO_SHORT, user, NULL); -+ } -+ -+ -+ -+ /* copy the query from userspace to kernel space & parse */ -+ buffer = kmalloc(*len, GFP_ATOMIC); -+ if(buffer == NULL) /* check for malloc failure */ -+ { -+ return handle_get_failure(0, 1, 0, ERROR_UNKNOWN, user, NULL); -+ } -+ copy_from_user(buffer, user, *len); -+ parse_get_request(buffer, &query); -+ -+ -+ -+ -+ -+ -+ /* -+ * retrieve data for this id and verify all variables are properly defined, just to be sure -+ * this is a kernel module -- it pays to be paranoid! -+ */ -+ spin_lock_bh(&bandwidth_lock); -+ -+ iam = (info_and_maps*)get_string_map_element(id_map, query.id); -+ -+ if(iam == NULL) -+ { -+ return handle_get_failure(0, 1, 1, ERROR_NO_ID, user, buffer); -+ } -+ if(iam->info == NULL || iam->ip_map == NULL) -+ { -+ return handle_get_failure(0, 1, 1, ERROR_NO_ID, user, buffer); -+ } -+ if(iam->info->num_intervals_to_save > 0 && iam->ip_history_map == NULL) -+ { -+ return handle_get_failure(0, 1, 1, ERROR_NO_ID, user, buffer); -+ } -+ -+ /* allocate ip list if this is first query */ -+ if(query.next_ip_index == 0 && query.ip == 0) -+ { -+ if(output_ip_list != NULL) -+ { -+ kfree(output_ip_list); -+ } -+ if(iam->info->type == BANDWIDTH_COMBINED) -+ { -+ output_ip_list_length = 1; -+ output_ip_list = (unsigned long*)kmalloc(sizeof(unsigned long), GFP_ATOMIC); -+ if(output_ip_list != NULL) { *output_ip_list = 0; } -+ } -+ else -+ { -+ output_ip_list = get_sorted_long_map_keys(iam->ip_map, &output_ip_list_length); -+ } -+ -+ if(output_ip_list == NULL) -+ { -+ return handle_get_failure(0, 1, 1, ERROR_UNKNOWN, user, buffer); -+ } -+ } -+ -+ /* if this is not first query do a sanity check -- make sure it's within bounds of allocated ip list */ -+ if(query.next_ip_index > 0 && (output_ip_list == NULL || query.next_ip_index > output_ip_list_length)) -+ { -+ return handle_get_failure(0, 1, 1, ERROR_UNKNOWN, user, buffer); -+ } -+ -+ -+ -+ -+ /* -+ // values only reset when a packet hits a rule, so -+ // reset may have expired without data being reset. -+ // So, test if we need to reset values to zero -+ */ -+ if(iam->info->reset_interval != BANDWIDTH_NEVER) -+ { -+ if(iam->info->next_reset < now) -+ { -+ //do reset -+ handle_interval_reset(iam, now); -+ } -+ } -+ -+ -+ -+ /* compute response & store it in buffer -+ * -+ * format of response: -+ * byte 1 : error code (0 for ok) -+ * bytes 2-5 : total_num_ips found in query (further gets may be necessary to retrieve them) -+ * bytes 6-9 : start_index, index (in a list of total_num_ips) of first ip in response -+ * bytes 10-13 : num_ips_in_response, number of ips in this response -+ * bytes 14-21 : reset_interval (helps deal with DST shifts in userspace) -+ * bytes 22-29 : reset_time (helps deal with DST shifts in userspace) -+ * byte 30 : reset_is_constant_interval (helps deal with DST shifts in userspace) -+ * remaining bytes contain blocks of ip data -+ * format is dependent on whether history was queried -+ * -+ * if history was NOT queried we have -+ * bytes 1-4 : ip -+ * bytes 5-12 : bandwidth -+ * -+ * if history WAS queried we have -+ * (note we are using 64 bit integers for time here -+ * even though time_t is 32 bits on most 32 bit systems -+ * just to be on the safe side) -+ * bytes 1-4 : ip -+ * bytes 4-8 : history_length number of history values (including current) -+ * bytes 9-16 : first start -+ * bytes 17-24 : first end -+ * bytes 25-32 : recent end -+ * 33 onward : list of 64 bit integers of length history_length -+ * -+ */ -+ error = buffer; -+ total_ips = (uint32_t*)(buffer+1); -+ start_index = (uint32_t*)(buffer+5); -+ num_ips_in_response = (uint32_t*)(buffer+9); -+ reset_interval = (uint64_t*)(buffer+13); -+ reset_time = (uint64_t*)(buffer+21); -+ reset_is_constant_interval = (char*)(buffer+29); -+ -+ *reset_interval = (uint64_t)iam->info->reset_interval; -+ *reset_time = (uint64_t)iam->info->reset_time; -+ *reset_is_constant_interval = iam->info->reset_is_constant_interval; -+ -+ current_output_index = 30; -+ if(query.ip != 0) -+ { -+ *error = add_ip_block( query.ip, -+ query.return_history, -+ iam, -+ buffer, -+ ¤t_output_index, -+ *len -+ ); -+ -+ *total_ips = *error == 0; -+ *start_index = 0; -+ *num_ips_in_response = *error == 0 ? 1 : 0; -+ } -+ else -+ { -+ uint32_t next_index = query.next_ip_index; -+ *error = ERROR_NONE; -+ *total_ips = output_ip_list_length; -+ *start_index = next_index; -+ *num_ips_in_response = 0; -+ while(*error == ERROR_NONE && next_index < output_ip_list_length) -+ { -+ uint32_t next_ip = output_ip_list[next_index]; -+ *error = add_ip_block( next_ip, -+ query.return_history, -+ iam, -+ buffer, -+ ¤t_output_index, -+ *len -+ ); -+ if(*error == ERROR_NONE) -+ { -+ *num_ips_in_response = *num_ips_in_response + 1; -+ next_index++; -+ } -+ } -+ if(*error == ERROR_BUFFER_TOO_SHORT && *num_ips_in_response > 0) -+ { -+ *error = ERROR_NONE; -+ } -+ if(next_index == output_ip_list_length) -+ { -+ kfree(output_ip_list); -+ output_ip_list = NULL; -+ output_ip_list_length = 0; -+ } -+ } -+ -+ spin_unlock_bh(&bandwidth_lock); -+ -+ copy_to_user(user, buffer, *len); -+ kfree(buffer); -+ -+ -+ -+ up(&userspace_lock); -+ -+ -+ return 0; -+} -+ -+ -+ -+ -+ -+/******************** -+ * Set functions -+ ********************/ -+ -+typedef struct set_header_struct -+{ -+ uint32_t total_ips; -+ uint32_t next_ip_index; -+ uint32_t num_ips_in_buffer; -+ unsigned char history_included; -+ unsigned char zero_unset_ips; -+ time_t last_backup; -+ char id[BANDWIDTH_MAX_ID_LENGTH]; -+} set_header; -+ -+static int handle_set_failure(int ret_value, int unlock_user_sem, int unlock_bandwidth_spin, unsigned char* free_buffer ); -+static void parse_set_header(unsigned char* input_buffer, set_header* header); -+static void set_single_ip_data(unsigned char history_included, info_and_maps* iam, unsigned char* buffer, uint32_t* buffer_index, time_t now); -+ -+static int handle_set_failure(int ret_value, int unlock_user_sem, int unlock_bandwidth_spin, unsigned char* free_buffer ) -+{ -+ if( free_buffer != NULL ) { kfree(free_buffer); } -+ set_in_progress = 0; -+ if(unlock_bandwidth_spin) { spin_unlock_bh(&bandwidth_lock); } -+ if(unlock_user_sem) { up(&userspace_lock); } -+ return ret_value; -+} -+ -+static void parse_set_header(unsigned char* input_buffer, set_header* header) -+{ -+ /* -+ * set header structure: -+ * bytes 1-4 : total_ips being set in this and subsequent requests -+ * bytes 5-8 : next_ip_index, first ip being set in this set command -+ * bytes 9-12 : num_ips_in_buffer, the number of ips in this set request -+ * byte 13 : history_included (whether history data is included, or just current data) -+ * byte 14 : zero_unset_ips, whether to zero all ips not included in this and subsequent requests -+ * bytes 15-22 : last_backup time (64 bit) -+ * bytes 23-23+BANDWIDTH_MAX_ID_LENGTH : id -+ * bytes 23+ : ip data -+ */ -+ -+ uint32_t* total_ips = (uint32_t*)(input_buffer+0); -+ uint32_t* next_ip_index = (uint32_t*)(input_buffer+4); -+ uint32_t* num_ips_in_buffer = (uint32_t*)(input_buffer+8); -+ unsigned char* history_included = (unsigned char*)(input_buffer+12); -+ unsigned char* zero_unset_ips = (unsigned char*)(input_buffer+13); -+ uint64_t* last_backup = (uint64_t*)(input_buffer+14); -+ -+ -+ header->total_ips = *total_ips; -+ header->next_ip_index = *next_ip_index; -+ header->num_ips_in_buffer = *num_ips_in_buffer; -+ header->history_included = *history_included; -+ header->zero_unset_ips = *zero_unset_ips; -+ header->last_backup = (time_t)*last_backup; -+ memcpy(header->id, input_buffer+22, BANDWIDTH_MAX_ID_LENGTH); -+ (header->id)[BANDWIDTH_MAX_ID_LENGTH-1] = '\0'; /* make sure id is null terminated no matter what */ -+ -+ #ifdef BANDWIDTH_DEBUG -+ printk("parsed set header:\n"); -+ printk(" total_ips = %d\n", header->total_ips); -+ printk(" next_ip_index = %d\n", header->next_ip_index); -+ printk(" num_ips_in_buffer = %d\n", header->num_ips_in_buffer); -+ printk(" zero_unset_ips = %d\n", header->zero_unset_ips); -+ printk(" last_backup = %ld\n", header->last_backup); -+ printk(" id = %s\n", header->id); -+ #endif -+} -+static void set_single_ip_data(unsigned char history_included, info_and_maps* iam, unsigned char* buffer, uint32_t* buffer_index, time_t now) -+{ -+ /* -+ * note that times stored within the module are adjusted so they are equal to seconds -+ * since unix epoch that corrosponds to the UTC wall-clock time (timezone offset 0) -+ * that is equal to the wall-clock time in the current time-zone. Incoming values must -+ * be adjusted similarly -+ */ -+ uint32_t ip = *( (uint32_t*)(buffer + *buffer_index) ); -+ -+ #ifdef BANDWIDTH_DEBUG -+ uint32_t* ipp = &ip; -+ printk("doing set for ip = %u.%u.%u.%u\n", *((unsigned char*)ipp), *(((unsigned char*)ipp)+1), *(((unsigned char*)ipp)+2), *(((unsigned char*)ipp)+3) ); -+ printk("ip index = %d\n", *buffer_index); -+ #endif -+ -+ if(history_included) -+ { -+ uint32_t num_history_nodes = *( (uint32_t*)(buffer + *buffer_index+4)); -+ if(iam->info->num_intervals_to_save > 0 && iam->ip_history_map != NULL) -+ { -+ time_t first_start = (time_t) *( (uint64_t*)(buffer + *buffer_index+8)); -+ /* time_t first_end = (time_t) *( (uint64_t*)(buffer + *buffer_index+16)); //not used */ -+ /* time_t last_end = (time_t) *( (uint64_t*)(buffer + *buffer_index+24)); //not used */ -+ time_t next_start; -+ time_t next_end; -+ uint32_t node_index; -+ uint32_t zero_count; -+ bw_history* history; -+ -+ -+ #ifdef BANDWIDTH_DEBUG -+ printk("setting history with first start = %ld, now = %ld\n", first_start, now); -+ #endif -+ -+ -+ *buffer_index = *buffer_index + (2*4) + (3*8); -+ -+ /* adjust for timezone */ -+ next_start = first_start - (60 * local_minutes_west); -+ next_end = get_next_reset_time(iam->info, next_start, next_start); -+ node_index=0; -+ zero_count=0; -+ history = NULL; -+ while(next_start < now) -+ { -+ uint64_t next_bw = 0; -+ if(node_index < num_history_nodes) -+ { -+ next_bw = *( (uint64_t*)(buffer + *buffer_index)); -+ *buffer_index = *buffer_index + 8; -+ } -+ zero_count = next_bw == 0 ? zero_count+1 : 0; -+ -+ if(node_index == 0 || history == NULL) -+ { -+ initialize_map_entries_for_ip(iam, ip, next_bw); -+ history = get_long_map_element(iam->ip_history_map, (unsigned long)ip); -+ } -+ else if(next_end < now) /* if this is most recent node, don't do update since last node is current bandwidth */ -+ { -+ update_history(history, next_start, next_end, iam->info); -+ (history->history_data)[ history->current_index ] = next_bw; -+ if(zero_count < history->max_nodes +2) -+ { -+ next_start = next_end; -+ next_end = get_next_reset_time(iam->info, next_start, next_start); -+ } -+ else -+ { -+ /* do history reset */ -+ history->first_start = 0; -+ history->first_end = 0; -+ history->last_end = 0; -+ history->num_nodes = 1; -+ history->non_zero_nodes = 1; -+ history->current_index = 0; -+ (history->history_data)[0] = 0; -+ -+ next_start = now; -+ next_end = get_next_reset_time(iam->info, now, next_start); -+ } -+ } -+ else /* if this is most recent node, we still need to exit loop*/ -+ { -+ break; -+ } -+ node_index++; -+ } -+ while(node_index < num_history_nodes) -+ { -+ *buffer_index = *buffer_index + 8; -+ node_index++; -+ } -+ if(history != NULL) -+ { -+ set_long_map_element(iam->ip_map, ip, (history->history_data + history->current_index) ); -+ iam->info->previous_reset = next_start; -+ iam->info->next_reset = next_end; -+ if(ip == 0) -+ { -+ iam->info->current_bandwidth = (history->history_data)[history->current_index]; -+ } -+ } -+ } -+ else -+ { -+ uint64_t bw; -+ *buffer_index = *buffer_index + (2*4) + (3*8) + ((num_history_nodes-1)*8); -+ bw = *( (uint64_t*)(buffer + *buffer_index)); -+ initialize_map_entries_for_ip(iam, ip, bw); /* automatically frees existing values if they exist */ -+ *buffer_index = *buffer_index + 8; -+ if(ip == 0) -+ { -+ iam->info->current_bandwidth = bw; -+ } -+ } -+ -+ } -+ else -+ { -+ uint64_t bw = *( (uint64_t*)(buffer + *buffer_index+4) ); -+ #ifdef BANDWIDTH_DEBUG -+ printk(" setting bw to %lld\n", bw ); -+ #endif -+ -+ -+ initialize_map_entries_for_ip(iam, ip, bw); /* automatically frees existing values if they exist */ -+ *buffer_index = *buffer_index + 12; -+ -+ if(ip == 0) -+ { -+ iam->info->current_bandwidth = bw; -+ } -+ } -+ -+ -+} -+ -+static int ipt_bandwidth_set_ctl(struct sock *sk, int cmd, void *user, u_int32_t len) -+{ -+ /* check for timezone shift & adjust if necessary */ -+ char* buffer; -+ set_header header; -+ info_and_maps* iam; -+ uint32_t buffer_index; -+ uint32_t next_ip_index; -+ time_t now = get_seconds(); -+ check_for_timezone_shift(now, 0); -+ check_for_backwards_time_shift(now); -+ now = now - local_seconds_west; /* Adjust for local timezone */ -+ -+ -+ /* just return right away if user buffer is too short to contain even the header */ -+ if(len < (3*4) + 2 + 8 + BANDWIDTH_MAX_ID_LENGTH) -+ { -+ #ifdef BANDWIDTH_DEBUG -+ printk("set error: buffer not large enough!\n"); -+ #endif -+ return 0; -+ } -+ -+ down(&userspace_lock); -+ set_in_progress = 1; -+ -+ buffer = kmalloc(len, GFP_ATOMIC); -+ if(buffer == NULL) /* check for malloc failure */ -+ { -+ return handle_set_failure(0, 1, 0, NULL); -+ } -+ copy_from_user(buffer, user, len); -+ parse_set_header(buffer, &header); -+ -+ -+ -+ -+ /* -+ * retrieve data for this id and verify all variables are properly defined, just to be sure -+ * this is a kernel module -- it pays to be paranoid! -+ */ -+ spin_lock_bh(&bandwidth_lock); -+ -+ -+ iam = (info_and_maps*)get_string_map_element(id_map, header.id); -+ if(iam == NULL) -+ { -+ return handle_set_failure(0, 1, 1, buffer); -+ } -+ if(iam->info == NULL || iam->ip_map == NULL) -+ { -+ return handle_set_failure(0, 1, 1, buffer); -+ } -+ if(iam->info->num_intervals_to_save > 0 && iam->ip_history_map == NULL) -+ { -+ return handle_set_failure(0, 1, 1, buffer); -+ } -+ -+ /* -+ * during set unconditionally set combined_bw to NULL -+ * if combined data (ip=0) exists after set exits cleanly, we will restore it -+ */ -+ iam->info->combined_bw = NULL; -+ -+ //if zero_unset_ips == 1 && next_ip_index == 0 -+ //then clear data for all ips for this id -+ if(header.zero_unset_ips && header.next_ip_index == 0) -+ { -+ //clear data -+ if(iam->info->num_intervals_to_save > 0) -+ { -+ while(iam->ip_map->num_elements > 0) -+ { -+ unsigned long key; -+ remove_smallest_long_map_element(iam->ip_map, &key); -+ /* ignore return value -- it's actually malloced in history, not here */ -+ } -+ while(iam->ip_history_map->num_elements > 0) -+ { -+ unsigned long key; -+ bw_history* history = remove_smallest_long_map_element(iam->ip_history_map, &key); -+ kfree(history->history_data); -+ kfree(history); -+ } -+ } -+ else -+ { -+ while(iam->ip_map->num_elements > 0) -+ { -+ unsigned long key; -+ uint64_t *bw = remove_smallest_long_map_element(iam->ip_map, &key); -+ kfree(bw); -+ } -+ } -+ } -+ -+ /* -+ * last_backup parameter is only relevant for case where we are not setting history -+ * and when we don't have a constant interval length or a specified reset_time (since in this case start time gets reset when rule is inserted and there is therefore no constant end) -+ * If num_intervals_to_save =0 and is_constant_interval=0, check it. If it's nonzero (0=ignore) and invalid, return. -+ */ -+ if(header.last_backup > 0 && iam->info->num_intervals_to_save == 0 && (iam->info->reset_is_constant_interval == 0 || iam->info->reset_time != 0) ) -+ { -+ time_t adjusted_last_backup_time = header.last_backup - (60 * local_minutes_west); -+ time_t next_reset_of_last_backup = get_next_reset_time(iam->info, adjusted_last_backup_time, adjusted_last_backup_time); -+ if(next_reset_of_last_backup != iam->info->next_reset) -+ { -+ return handle_set_failure(0, 1, 1, buffer); -+ } -+ } -+ -+ -+ /* -+ * iterate over each ip block in buffer, -+ * loading data into necessary kerenel-space data structures -+ */ -+ buffer_index = (3*4) + 1 + 1 + 8 + BANDWIDTH_MAX_ID_LENGTH; -+ next_ip_index = header.next_ip_index; -+ -+ while(next_ip_index < header.num_ips_in_buffer) -+ { -+ set_single_ip_data(header.history_included, iam, buffer, &buffer_index, now); -+ next_ip_index++; -+ } -+ -+ if (next_ip_index == header.total_ips) -+ { -+ set_in_progress = 0; -+ } -+ -+ /* set combined_bw */ -+ iam->info->combined_bw = (uint64_t*)get_long_map_element(iam->ip_map, 0); -+ -+ kfree(buffer); -+ spin_unlock_bh(&bandwidth_lock); -+ up(&userspace_lock); -+ return 0; -+} -+static int checkentry(const struct xt_mtchk_param *par) -+{ -+ -+ -+ struct ipt_bandwidth_info *info = (struct ipt_bandwidth_info*)(par->matchinfo); -+ -+ -+ -+ #ifdef BANDWIDTH_DEBUG -+ printk("checkentry called\n"); -+ #endif -+ -+ -+ -+ -+ -+ if(info->ref_count == NULL) /* first instance, we're inserting rule */ -+ { -+ struct ipt_bandwidth_info *master_info = (struct ipt_bandwidth_info*)kmalloc(sizeof(struct ipt_bandwidth_info), GFP_ATOMIC); -+ info->ref_count = (unsigned long*)kmalloc(sizeof(unsigned long), GFP_ATOMIC); -+ -+ if(info->ref_count == NULL) /* deal with kmalloc failure */ -+ { -+ printk("ipt_bandwidth: kmalloc failure in checkentry!\n"); -+ return 0; -+ } -+ *(info->ref_count) = 1; -+ info->non_const_self = master_info; -+ info->hashed_id = sdbm_string_hash(info->id); -+ info->iam = NULL; -+ info->combined_bw = NULL; -+ -+ memcpy(master_info->id, info->id, BANDWIDTH_MAX_ID_LENGTH); -+ master_info->type = info->type; -+ master_info->check_type = info->check_type; -+ master_info->local_subnet = info->local_subnet; -+ master_info->local_subnet_mask = info->local_subnet_mask; -+ master_info->cmp = info->cmp; -+ master_info->reset_is_constant_interval = info->reset_is_constant_interval; -+ master_info->reset_interval = info->reset_interval; -+ master_info->reset_time = info->reset_time; -+ master_info->bandwidth_cutoff = info->bandwidth_cutoff; -+ master_info->current_bandwidth = info->current_bandwidth; -+ master_info->next_reset = info->next_reset; -+ master_info->previous_reset = info->previous_reset; -+ master_info->last_backup_time = info->last_backup_time; -+ master_info->num_intervals_to_save = info->num_intervals_to_save; -+ -+ master_info->hashed_id = info->hashed_id; -+ master_info->iam = info->iam; -+ master_info->combined_bw = info->combined_bw; -+ master_info->non_const_self = info->non_const_self; -+ master_info->ref_count = info->ref_count; -+ -+ #ifdef BANDWIDTH_DEBUG -+ printk(" after increment, ref count = %ld\n", *(info->ref_count) ); -+ #endif -+ -+ if(info->cmp != BANDWIDTH_CHECK) -+ { -+ info_and_maps *iam; -+ -+ down(&userspace_lock); -+ spin_lock_bh(&bandwidth_lock); -+ -+ -+ -+ iam = (info_and_maps*)get_string_map_element(id_map, info->id); -+ if(iam != NULL) -+ { -+ printk("ipt_bandwidth: error, \"%s\" is a duplicate id\n", info->id); -+ spin_unlock_bh(&bandwidth_lock); -+ up(&userspace_lock); -+ return 0; -+ } -+ -+ if(info->reset_interval != BANDWIDTH_NEVER) -+ { -+ time_t now = get_seconds(); -+ if(now != last_local_mw_update ) -+ { -+ check_for_timezone_shift(now, 1); -+ } -+ -+ -+ now = now - (60 * local_minutes_west); /* Adjust for local timezone */ -+ info->previous_reset = now; -+ master_info->previous_reset = now; -+ if(info->next_reset == 0) -+ { -+ info->next_reset = get_next_reset_time(info, now, now); -+ master_info->next_reset = info->next_reset; -+ /* -+ * if we specify last backup time, check that next reset is consistent, -+ * otherwise reset current_bandwidth to 0 -+ * -+ * only applies to combined type -- otherwise we need to handle setting bandwidth -+ * through userspace library -+ */ -+ if(info->last_backup_time != 0 && info->type == BANDWIDTH_COMBINED) -+ { -+ time_t adjusted_last_backup_time = info->last_backup_time - (60 * local_minutes_west); -+ time_t next_reset_of_last_backup = get_next_reset_time(info, adjusted_last_backup_time, adjusted_last_backup_time); -+ if(next_reset_of_last_backup != info->next_reset) -+ { -+ info->current_bandwidth = 0; -+ master_info->current_bandwidth = 0; -+ } -+ info->last_backup_time = 0; -+ master_info->last_backup_time = 0; -+ } -+ } -+ } -+ -+ iam = (info_and_maps*)kmalloc( sizeof(info_and_maps), GFP_ATOMIC); -+ if(iam == NULL) /* handle kmalloc failure */ -+ { -+ printk("ipt_bandwidth: kmalloc failure in checkentry!\n"); -+ spin_unlock_bh(&bandwidth_lock); -+ up(&userspace_lock); -+ return 0; -+ } -+ iam->ip_map = initialize_long_map(); -+ if(iam->ip_map == NULL) /* handle kmalloc failure */ -+ { -+ printk("ipt_bandwidth: kmalloc failure in checkentry!\n"); -+ spin_unlock_bh(&bandwidth_lock); -+ up(&userspace_lock); -+ return 0; -+ } -+ iam->ip_history_map = NULL; -+ if(info->num_intervals_to_save > 0) -+ { -+ iam->ip_history_map = initialize_long_map(); -+ if(iam->ip_history_map == NULL) /* handle kmalloc failure */ -+ { -+ printk("ipt_bandwidth: kmalloc failure in checkentry!\n"); -+ spin_unlock_bh(&bandwidth_lock); -+ up(&userspace_lock); -+ return 0; -+ } -+ } -+ -+ -+ iam->info = master_info; -+ set_string_map_element(id_map, info->id, iam); -+ -+ info->iam = (void*)iam; -+ master_info->iam = (void*)iam; -+ -+ -+ spin_unlock_bh(&bandwidth_lock); -+ up(&userspace_lock); -+ } -+ } -+ -+ else -+ { -+ /* info->non_const_self = info; */ -+ -+ -+ *(info->ref_count) = *(info->ref_count) + 1; -+ #ifdef BANDWIDTH_DEBUG -+ printk(" after increment, ref count = %ld\n", *(info->ref_count) ); -+ #endif -+ -+ -+ /* -+ if(info->cmp != BANDWIDTH_CHECK) -+ { -+ info_and_maps* iam; -+ down(&userspace_lock); -+ spin_lock_bh(&bandwidth_lock); -+ iam = (info_and_maps*)get_string_map_element(id_map, info->id); -+ if(iam != NULL) -+ { -+ iam->info = info; -+ } -+ spin_unlock_bh(&bandwidth_lock); -+ up(&userspace_lock); -+ } -+ */ -+ } -+ -+ #ifdef BANDWIDTH_DEBUG -+ printk("checkentry complete\n"); -+ #endif -+ return 0; -+} -+ -+static void destroy(const struct xt_mtdtor_param *par) -+{ -+ -+ struct ipt_bandwidth_info *info = (struct ipt_bandwidth_info*)(par->matchinfo); -+ -+ #ifdef BANDWIDTH_DEBUG -+ printk("destroy called\n"); -+ #endif -+ -+ *(info->ref_count) = *(info->ref_count) - 1; -+ -+ #ifdef BANDWIDTH_DEBUG -+ printk(" after decrement refcount = %ld\n", *(info->ref_count)); -+ #endif -+ -+ if(*(info->ref_count) == 0) -+ { -+ info_and_maps* iam; -+ down(&userspace_lock); -+ spin_lock_bh(&bandwidth_lock); -+ -+ info->combined_bw = NULL; -+ iam = (info_and_maps*)remove_string_map_element(id_map, info->id); -+ if(iam != NULL && info->cmp != BANDWIDTH_CHECK) -+ { -+ unsigned long num_destroyed; -+ if(iam->ip_map != NULL && iam->ip_history_map != NULL) -+ { -+ unsigned long history_index = 0; -+ bw_history** histories_to_free; -+ -+ destroy_long_map(iam->ip_map, DESTROY_MODE_IGNORE_VALUES, &num_destroyed); -+ -+ histories_to_free = (bw_history**)destroy_long_map(iam->ip_history_map, DESTROY_MODE_RETURN_VALUES, &num_destroyed); -+ -+ /* num_destroyed will be 0 if histories_to_free is null after malloc failure, so this is safe */ -+ for(history_index = 0; history_index < num_destroyed; history_index++) -+ { -+ bw_history* h = histories_to_free[history_index]; -+ if(h != NULL) -+ { -+ kfree(h->history_data); -+ kfree(h); -+ } -+ } -+ -+ } -+ else if(iam->ip_map != NULL) -+ { -+ destroy_long_map(iam->ip_map, DESTROY_MODE_FREE_VALUES, &num_destroyed); -+ } -+ kfree(iam); -+ /* info portion of iam gets taken care of automatically */ -+ } -+ kfree(info->ref_count); -+ kfree(info->non_const_self); -+ -+ spin_unlock_bh(&bandwidth_lock); -+ up(&userspace_lock); -+ } -+ -+ #ifdef BANDWIDTH_DEBUG -+ printk("destroy complete\n"); -+ #endif -+} -+ -+static struct nf_sockopt_ops ipt_bandwidth_sockopts = -+{ -+ .pf = PF_INET, -+ .set_optmin = BANDWIDTH_SET, -+ .set_optmax = BANDWIDTH_SET+1, -+ .set = ipt_bandwidth_set_ctl, -+ .get_optmin = BANDWIDTH_GET, -+ .get_optmax = BANDWIDTH_GET+1, -+ .get = ipt_bandwidth_get_ctl -+}; -+ -+ -+static struct xt_match bandwidth_match __read_mostly = -+{ -+ .name = "bandwidth", -+ .match = match, -+ .family = AF_INET, -+ .matchsize = sizeof(struct ipt_bandwidth_info), -+ .checkentry = checkentry, -+ .destroy = destroy, -+ .me = THIS_MODULE, -+}; -+ -+static int __init init(void) -+{ -+ /* Register setsockopt */ -+ if (nf_register_sockopt(&ipt_bandwidth_sockopts) < 0) -+ { -+ printk("ipt_bandwidth: Can't register sockopts. Aborting\n"); -+ } -+ bandwidth_record_max = get_bw_record_max(); -+ local_minutes_west = old_minutes_west = sys_tz.tz_minuteswest; -+ local_seconds_west = local_minutes_west*60; -+ last_local_mw_update = get_seconds(); -+ if(local_seconds_west > last_local_mw_update) -+ { -+ /* we can't let adjusted time be < 0 -- pretend timezone is still UTC */ -+ local_minutes_west = 0; -+ local_seconds_west = 0; -+ } -+ -+ id_map = initialize_string_map(0); -+ if(id_map == NULL) /* deal with kmalloc failure */ -+ { -+ printk("id map is null, returning -1\n"); -+ return -1; -+ } -+ -+ -+ return xt_register_match(&bandwidth_match); -+} -+ -+static void __exit fini(void) -+{ -+ down(&userspace_lock); -+ spin_lock_bh(&bandwidth_lock); -+ if(id_map != NULL) -+ { -+ unsigned long num_returned; -+ info_and_maps **iams = (info_and_maps**)destroy_string_map(id_map, DESTROY_MODE_RETURN_VALUES, &num_returned); -+ int iam_index; -+ for(iam_index=0; iam_index < num_returned; iam_index++) -+ { -+ info_and_maps* iam = iams[iam_index]; -+ long_map* ip_map = iam->ip_map; -+ unsigned long num_destroyed; -+ destroy_long_map(ip_map, DESTROY_MODE_FREE_VALUES, &num_destroyed); -+ kfree(iam); -+ /* info portion of iam gets taken care of automatically */ -+ } -+ } -+ nf_unregister_sockopt(&ipt_bandwidth_sockopts); -+ xt_unregister_match(&bandwidth_match); -+ spin_unlock_bh(&bandwidth_lock); -+ up(&userspace_lock); -+ -+} -+ -+module_init(init); -+module_exit(fini); -+ - ---- /dev/null -+++ b/net/ipv4/netfilter/ipt_bandwidth.mod.c -@@ -0,0 +1,20 @@ -+#include -+#include -+#include -+ -+MODULE_INFO(vermagic, VERMAGIC_STRING); -+ -+struct module __this_module -+__attribute__((section(".gnu.linkonce.this_module"))) = { -+ .name = KBUILD_MODNAME, -+ .init = init_module, -+#ifdef CONFIG_MODULE_UNLOAD -+ .exit = cleanup_module, -+#endif -+}; -+ -+static const char __module_depends[] -+__attribute_used__ -+__attribute__((section(".modinfo"))) = -+"depends="; -+ ---- /dev/null -+++ b/net/ipv4/netfilter/ipt_timerange.c -@@ -0,0 +1,142 @@ -+/* timerange -- An iptables extension to match multiple timeranges within a week -+ * Originally designed for use with Gargoyle router firmware (gargoyle-router.com) -+ * -+ * -+ * Copyright © 2009-2010 by Eric Bishop -+ * -+ * This file is free software: you may copy, redistribute and/or modify it -+ * under the terms of the GNU General Public License as published by the -+ * Free Software Foundation, either version 2 of the License, or (at your -+ * option) any later version. -+ * -+ * This file is distributed in the hope that it will be useful, but -+ * WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#include -+ -+ -+#include -+ -+#include -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Eric Bishop"); -+MODULE_DESCRIPTION("Match time ranges, designed for use with Gargoyle web interface (www.gargoyle-router.com)"); -+ -+ -+extern struct timezone sys_tz; -+ -+ -+static bool match(const struct sk_buff *skb, struct xt_action_param *par) -+{ -+ const struct ipt_timerange_info *info = (const struct ipt_timerange_info*)(par->matchinfo); -+ -+ -+ time_t stamp_time; -+ int weekday; -+ int seconds_since_midnight; -+ int test_index; -+ int match_found; -+ -+ struct timeval test_time; -+ -+ do_gettimeofday(&test_time); -+ stamp_time = test_time.tv_sec; -+ stamp_time = stamp_time - (60 * sys_tz.tz_minuteswest); /* Adjust for local timezone */ -+ seconds_since_midnight = stamp_time % 86400; /* 86400 seconds per day */ -+ weekday = (4 + (stamp_time/86400)) % 7; /* 1970-01-01 (time=0) was a Thursday (4). */ -+ -+ /* -+ printk("time=%d, since midnight = %d, day=%d, minuteswest=%d\n", stamp_time, seconds_since_midnight, weekday, sys_tz.tz_minuteswest); -+ */ -+ -+ match_found = 0; -+ if(info->type == HOURS) -+ { -+ for(test_index=0; info->ranges[test_index] != -1 && match_found == 0 && seconds_since_midnight >= info->ranges[test_index]; test_index=test_index+2) -+ { -+ match_found = seconds_since_midnight >= info->ranges[test_index] && seconds_since_midnight <= info->ranges[test_index+1] ? 1 : match_found; -+ } -+ } -+ else if(info->type == WEEKDAYS) -+ { -+ match_found = info->days[weekday]; -+ } -+ else if(info->type == DAYS_HOURS) -+ { -+ match_found = info->days[weekday]; -+ if(match_found == 1) -+ { -+ match_found = 0; -+ for(test_index=0; info->ranges[test_index] != -1 && match_found == 0 && seconds_since_midnight >= info->ranges[test_index]; test_index=test_index+2) -+ { -+ match_found = seconds_since_midnight >= info->ranges[test_index] && seconds_since_midnight <= info->ranges[test_index+1] ? 1 : match_found; -+ } -+ } -+ } -+ else if(info->type == WEEKLY_RANGE) -+ { -+ time_t seconds_since_sunday_midnight = seconds_since_midnight + (weekday*86400); -+ for(test_index=0; info->ranges[test_index] != -1 && match_found == 0 && seconds_since_sunday_midnight >= info->ranges[test_index]; test_index=test_index+2) -+ { -+ match_found = seconds_since_sunday_midnight >= info->ranges[test_index] && seconds_since_sunday_midnight <= info->ranges[test_index+1] ? 1 : match_found; -+ } -+ -+ } -+ -+ match_found = info->invert == 0 ? match_found : !match_found; -+ return match_found; -+} -+ -+ -+static int checkentry(const struct xt_mtchk_param *par) -+{ -+ return 0; -+} -+ -+ -+static struct xt_match timerange_match __read_mostly = -+{ -+ .name = "timerange", -+ .match = match, -+ .family = AF_INET, -+ .matchsize = sizeof(struct ipt_timerange_info), -+ .checkentry = checkentry, -+ .me = THIS_MODULE, -+}; -+ -+static int __init init(void) -+{ -+ return xt_register_match(&timerange_match); -+} -+ -+static void __exit fini(void) -+{ -+ xt_unregister_match(&timerange_match); -+} -+ -+module_init(init); -+module_exit(fini); -+ - ---- /dev/null -+++ b/net/ipv4/netfilter/ipt_timerange.mod.c -@@ -0,0 +1,20 @@ -+#include -+#include -+#include -+ -+MODULE_INFO(vermagic, VERMAGIC_STRING); -+ -+struct module __this_module -+__attribute__((section(".gnu.linkonce.this_module"))) = { -+ .name = KBUILD_MODNAME, -+ .init = init_module, -+#ifdef CONFIG_MODULE_UNLOAD -+ .exit = cleanup_module, -+#endif -+}; -+ -+static const char __module_depends[] -+__attribute_used__ -+__attribute__((section(".modinfo"))) = -+"depends="; -+ ---- /dev/null -+++ b/net/ipv4/netfilter/ipt_webmon.c -@@ -0,0 +1,1319 @@ -+/* webmon -- A netfilter module to match URLs in HTTP(S) requests -+ * This module can match using string match or regular expressions -+ * Originally designed for use with Gargoyle router firmware (gargoyle-router.com) -+ * -+ * -+ * Copyright © 2008-2011 by Eric Bishop -+ * -+ * This file is free software: you may copy, redistribute and/or modify it -+ * under the terms of the GNU General Public License as published by the -+ * Free Software Foundation, either version 2 of the License, or (at your -+ * option) any later version. -+ * -+ * This file is distributed in the hope that it will be useful, but -+ * WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#include "webmon_deps/tree_map.h" -+ -+ -+#include -+ -+ -+#include -+#include -+ -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Eric Bishop"); -+MODULE_DESCRIPTION("Monitor URL in HTTP(S) Requests, designed for use with Gargoyle web interface (www.gargoyle-router.com)"); -+ -+#define NIPQUAD(addr) \ -+ ((unsigned char *)&addr)[0], \ -+ ((unsigned char *)&addr)[1], \ -+ ((unsigned char *)&addr)[2], \ -+ ((unsigned char *)&addr)[3] -+#define STRIP "%u.%u.%u.%u" -+ -+typedef struct qn -+{ -+ uint32_t src_ip; -+ char* value; -+ struct timeval time; -+ struct qn* next; -+ struct qn* previous; -+} queue_node; -+ -+typedef struct -+{ -+ queue_node* first; -+ queue_node* last; -+ int length; -+} queue; -+ -+static string_map* domain_map = NULL; -+static queue* recent_domains = NULL; -+ -+static string_map* search_map = NULL; -+static queue* recent_searches = NULL; -+ -+ -+static int max_domain_queue_length = 5; -+static int max_search_queue_length = 5; -+ -+static spinlock_t webmon_lock = __SPIN_LOCK_UNLOCKED(webmon_lock);; -+ -+ -+static void update_queue_node_time(queue_node* update_node, queue* full_queue) -+{ -+ struct timeval t; -+ do_gettimeofday(&t); -+ update_node->time = t; -+ -+ /* move to front of queue if not already at front of queue */ -+ if(update_node->previous != NULL) -+ { -+ queue_node* p = update_node->previous; -+ queue_node* n = update_node->next; -+ p->next = n; -+ if(n != NULL) -+ { -+ n->previous = p; -+ } -+ else -+ { -+ full_queue->last = p; -+ } -+ update_node->previous = NULL; -+ update_node->next = full_queue->first; -+ full_queue->first->previous = update_node; -+ full_queue->first = update_node; -+ } -+} -+ -+void add_queue_node(uint32_t src_ip, char* value, queue* full_queue, string_map* queue_index, char* queue_index_key, uint32_t max_queue_length ) -+{ -+ -+ queue_node *new_node = (queue_node*)kmalloc(sizeof(queue_node), GFP_ATOMIC); -+ char* dyn_value = kernel_strdup(value); -+ struct timeval t; -+ -+ -+ if(new_node == NULL || dyn_value == NULL) -+ { -+ if(dyn_value) { kfree(dyn_value); } -+ if(new_node) { kfree(new_node); }; -+ -+ return; -+ } -+ set_map_element(queue_index, queue_index_key, (void*)new_node); -+ -+ -+ do_gettimeofday(&t); -+ new_node->time = t; -+ new_node->src_ip = src_ip; -+ new_node->value = dyn_value; -+ new_node->previous = NULL; -+ -+ new_node->next = full_queue->first; -+ if(full_queue->first != NULL) -+ { -+ full_queue->first->previous = new_node; -+ } -+ full_queue->first = new_node; -+ full_queue->last = (full_queue->last == NULL) ? new_node : full_queue->last ; -+ full_queue->length = full_queue->length + 1; -+ -+ if( full_queue->length > max_queue_length ) -+ { -+ queue_node *old_node = full_queue->last; -+ full_queue->last = old_node->previous; -+ full_queue->last->next = NULL; -+ full_queue->first = old_node->previous == NULL ? NULL : full_queue->first; /*shouldn't be needed, but just in case...*/ -+ full_queue->length = full_queue->length - 1; -+ -+ sprintf(queue_index_key, STRIP"@%s", NIPQUAD(old_node->src_ip), old_node->value); -+ remove_map_element(queue_index, queue_index_key); -+ -+ kfree(old_node->value); -+ kfree(old_node); -+ } -+ -+ /* -+ queue_node* n = full_queue->first; -+ while(n != NULL) -+ { -+ printf("%ld\t%s\t%s\t%s\n", (unsigned long)n->time, n->src_ip, n->dst_ip, n->domain); -+ n = (queue_node*)n->next; -+ } -+ printf("\n\n"); -+ */ -+} -+ -+void destroy_queue(queue* q) -+{ -+ queue_node *last_node = q->last; -+ while(last_node != NULL) -+ { -+ queue_node *previous_node = last_node->previous; -+ free(last_node->value); -+ free(last_node); -+ last_node = previous_node; -+ } -+ free(q); -+} -+ -+ -+int strnicmp(const char * cs,const char * ct,size_t count) -+{ -+ register signed char __res = 0; -+ -+ while (count) -+ { -+ if ((__res = toupper( *cs ) - toupper( *ct++ ) ) != 0 || !*cs++) -+ { -+ break; -+ } -+ count--; -+ } -+ return __res; -+} -+ -+char *strnistr(const char *s, const char *find, size_t slen) -+{ -+ char c, sc; -+ size_t len; -+ -+ -+ if ((c = *find++) != '\0') -+ { -+ len = strlen(find); -+ do -+ { -+ do -+ { -+ if (slen < 1 || (sc = *s) == '\0') -+ { -+ return (NULL); -+ } -+ --slen; -+ ++s; -+ } -+ while ( toupper(sc) != toupper(c)); -+ -+ if (len > slen) -+ { -+ return (NULL); -+ } -+ } -+ while (strnicmp(s, find, len) != 0); -+ -+ s--; -+ } -+ return ((char *)s); -+} -+ -+/* NOTE: This is not quite real edit distance -- all differences are assumed to be in one contiguous block -+ * If differences are not in a contiguous block computed edit distance will be greater than real edit distance. -+ * Edit distance computed here is an upper bound on real edit distance. -+ */ -+int within_edit_distance(char *s1, char *s2, int max_edit) -+{ -+ int ret = 0; -+ if(s1 != NULL && s2 != NULL) -+ { -+ int edit1 = strlen(s1); -+ int edit2 = strlen(s2); -+ char* s1sp = s1; -+ char* s2sp = s2; -+ char* s1ep = s1 + (edit1-1); -+ char* s2ep = s2 + (edit2-1); -+ while(*s1sp != '\0' && *s2sp != '\0' && *s1sp == *s2sp) -+ { -+ s1sp++; -+ s2sp++; -+ edit1--; -+ edit2--; -+ } -+ -+ /* if either is zero we got to the end of one of the strings */ -+ while(s1ep > s1sp && s2ep > s2sp && *s1ep == *s2ep) -+ { -+ s1ep--; -+ s2ep--; -+ edit1--; -+ edit2--; -+ } -+ ret = edit1 <= max_edit && edit2 <= max_edit ? 1 : 0; -+ } -+ return ret; -+} -+ -+ -+/* -+ * line is the line to be parsed -- it is not modified in any way -+ * max_pieces indicates number of pieces to return, if negative this is determined dynamically -+ * include_remainder_at_max indicates whether the last piece, when max pieces are reached, -+ * should be what it would normally be (0) or the entire remainder of the line (1) -+ * if max_pieces < 0 this parameter is ignored -+ * -+ * -+ * returns all non-separator pieces in a line -+ * result is dynamically allocated, MUST be freed after call-- even if -+ * line is empty (you still get a valid char** pointer to to a NULL char*) -+ */ -+char** split_on_separators(char* line, char* separators, int num_separators, int max_pieces, int include_remainder_at_max, unsigned long *num_pieces) -+{ -+ char** split; -+ -+ *num_pieces = 0; -+ if(line != NULL) -+ { -+ int split_index; -+ int non_separator_found; -+ char* dup_line; -+ char* start; -+ -+ if(max_pieces < 0) -+ { -+ /* count number of separator characters in line -- this count + 1 is an upperbound on number of pieces */ -+ int separator_count = 0; -+ int line_index; -+ for(line_index = 0; line[line_index] != '\0'; line_index++) -+ { -+ int sep_index; -+ int found = 0; -+ for(sep_index =0; found == 0 && sep_index < num_separators; sep_index++) -+ { -+ found = separators[sep_index] == line[line_index] ? 1 : 0; -+ } -+ separator_count = separator_count+ found; -+ } -+ max_pieces = separator_count + 1; -+ } -+ split = (char**)malloc((1+max_pieces)*sizeof(char*)); -+ split_index = 0; -+ split[split_index] = NULL; -+ -+ -+ dup_line = strdup(line); -+ start = dup_line; -+ non_separator_found = 0; -+ while(non_separator_found == 0) -+ { -+ int matches = 0; -+ int sep_index; -+ for(sep_index =0; sep_index < num_separators; sep_index++) -+ { -+ matches = matches == 1 || separators[sep_index] == start[0] ? 1 : 0; -+ } -+ non_separator_found = matches==0 || start[0] == '\0' ? 1 : 0; -+ if(non_separator_found == 0) -+ { -+ start++; -+ } -+ } -+ -+ while(start[0] != '\0' && split_index < max_pieces) -+ { -+ /* find first separator index */ -+ int first_separator_index = 0; -+ int separator_found = 0; -+ while( separator_found == 0 ) -+ { -+ int sep_index; -+ for(sep_index =0; separator_found == 0 && sep_index < num_separators; sep_index++) -+ { -+ separator_found = separators[sep_index] == start[first_separator_index] || start[first_separator_index] == '\0' ? 1 : 0; -+ } -+ if(separator_found == 0) -+ { -+ first_separator_index++; -+ } -+ } -+ -+ /* copy next piece to split array */ -+ if(first_separator_index > 0) -+ { -+ char* next_piece = NULL; -+ if(split_index +1 < max_pieces || include_remainder_at_max <= 0) -+ { -+ next_piece = (char*)malloc((first_separator_index+1)*sizeof(char)); -+ memcpy(next_piece, start, first_separator_index); -+ next_piece[first_separator_index] = '\0'; -+ } -+ else -+ { -+ next_piece = strdup(start); -+ } -+ split[split_index] = next_piece; -+ split[split_index+1] = NULL; -+ split_index++; -+ } -+ -+ -+ /* find next non-separator index, indicating start of next piece */ -+ start = start+ first_separator_index; -+ non_separator_found = 0; -+ while(non_separator_found == 0) -+ { -+ int matches = 0; -+ int sep_index; -+ for(sep_index =0; sep_index < num_separators; sep_index++) -+ { -+ matches = matches == 1 || separators[sep_index] == start[0] ? 1 : 0; -+ } -+ non_separator_found = matches==0 || start[0] == '\0' ? 1 : 0; -+ if(non_separator_found == 0) -+ { -+ start++; -+ } -+ } -+ } -+ free(dup_line); -+ *num_pieces = split_index; -+ } -+ else -+ { -+ split = (char**)malloc((1)*sizeof(char*)); -+ split[0] = NULL; -+ } -+ return split; -+} -+ -+ -+ -+static void extract_url(const unsigned char* packet_data, int packet_length, char* domain, char* path) -+{ -+ -+ int path_start_index; -+ int path_end_index; -+ int last_header_index; -+ char last_two_buf[2]; -+ int end_found; -+ char* domain_match; -+ char* start_ptr; -+ -+ domain[0] = '\0'; -+ path[0] = '\0'; -+ -+ -+ /* get path portion of URL */ -+ start_ptr = strnistr((char*)packet_data, " ", packet_length); -+ if(start_ptr == NULL) -+ { -+ return; -+ } -+ -+ path_start_index = (int)(start_ptr - (char*)packet_data); -+ start_ptr = strnistr((char*)(packet_data+path_start_index), " ", packet_length-(path_start_index+2)); -+ if(start_ptr == NULL) -+ { -+ return; -+ } -+ -+ while( packet_data[path_start_index] == ' ') -+ { -+ path_start_index++; -+ } -+ path_end_index= (int)(strstr( (char*)(packet_data+path_start_index), " ") - (char*)packet_data); -+ if(path_end_index > 0) -+ { -+ int path_length = path_end_index-path_start_index; -+ path_length = path_length < 625 ? path_length : 624; /* prevent overflow */ -+ memcpy(path, packet_data+path_start_index, path_length); -+ path[ path_length] = '\0'; -+ } -+ else -+ { -+ return; -+ } -+ -+ /* get header length */ -+ last_header_index = 2; -+ memcpy(last_two_buf,(char*)packet_data, 2); -+ end_found = 0; -+ while(end_found == 0 && last_header_index < packet_length) -+ { -+ char next = (char)packet_data[last_header_index]; -+ if(next == '\n') -+ { -+ end_found = last_two_buf[1] == '\n' || (last_two_buf[0] == '\n' && last_two_buf[1] == '\r') ? 1 : 0; -+ } -+ if(end_found == 0) -+ { -+ last_two_buf[0] = last_two_buf[1]; -+ last_two_buf[1] = next; -+ last_header_index++; -+ } -+ } -+ -+ /* get domain portion of URL */ -+ domain_match = strnistr( (char*)packet_data, "Host:", last_header_index); -+ if(domain_match != NULL) -+ { -+ int domain_end_index; -+ domain_match = domain_match + 5; /* character after "Host:" */ -+ while(domain_match[0] == ' ' && ( (char*)domain_match - (char*)packet_data) < last_header_index) -+ { -+ domain_match = domain_match+1; -+ } -+ -+ domain_end_index = 0; -+ while( domain_match[domain_end_index] != '\n' && -+ domain_match[domain_end_index] != '\r' && -+ domain_match[domain_end_index] != ' ' && -+ domain_match[domain_end_index] != ':' && -+ ((char*)domain_match - (char*)packet_data)+domain_end_index < last_header_index -+ ) -+ { -+ domain_end_index++; -+ } -+ domain_end_index = domain_end_index < 625 ? domain_end_index : 624; /* prevent overflow */ -+ memcpy(domain, domain_match, domain_end_index); -+ domain[domain_end_index] = '\0'; -+ -+ for(domain_end_index=0; domain[domain_end_index] != '\0'; domain_end_index++) -+ { -+ domain[domain_end_index] = (char)tolower(domain[domain_end_index]); -+ } -+ } -+} -+ -+static void extract_url_https(const unsigned char* packet_data, int packet_length, char* domain) -+{ -+ //TLSv1.2 Record Layer - All calculations based on this -+ //We want to abuse the SNI (Server Name Indication) extension to harvest likely URLs -+ //Content Type = 0x16 (22) is a "Handshake", HandShake Type 0x01 (1) is a "Client Hello" -+ int x, packet_limit; -+ unsigned short cslen, ext_type, ext_len, maxextlen; -+ unsigned char conttype, hndshktype, sidlen, cmplen; -+ unsigned char* packet_ptr; -+ -+ domain[0] = '\0'; -+ packet_ptr = packet_data; -+ -+ if (packet_length < 43) -+ { -+ /*printk("Packet less than 43 bytes, exiting\n");*/ -+ return; -+ } -+ conttype = packet_data[0]; -+ hndshktype = packet_data[5]; -+ sidlen = packet_data[43]; -+ /*printk("conttype=%d, hndshktype=%d, sidlen=%d ",conttype,hndshktype,sidlen);*/ -+ if(conttype != 22) -+ { -+ /*printk("conttype not 22, exiting\n");*/ -+ return; -+ } -+ if(hndshktype != 1) -+ { -+ /*printk("hndshktype not 1, exiting\n");*/ -+ return; //We aren't in a Client Hello -+ } -+ -+ packet_ptr = packet_data + 1 + 43 + sidlen; //Skip to Cipher Suites Length -+ cslen = ntohs(*(unsigned short*)packet_ptr); //Length of Cipher Suites (2 byte) -+ packet_ptr = packet_ptr + 2 + cslen; //Skip to Compression Methods -+ cmplen = *packet_ptr; //Length of Compression Methods (1 byte) -+ packet_ptr = packet_ptr + 1 + cmplen; //Skip to Extensions Length **IMPORTANT** -+ maxextlen = ntohs(*(unsigned short*)packet_ptr); //Length of extensions (2 byte) -+ packet_ptr = packet_ptr + 2; //Skip to beginning of first extension and start looping -+ ext_type = 1; -+ /*printk("cslen=%d, cmplen=%d, maxextlen=%d, pktlen=%d,ptrpos=%d\n",cslen,cmplen,maxextlen,packet_length,packet_ptr - packet_data);*/ -+ //Limit the pointer bounds to the smaller of either the extensions length or the packet length -+ packet_limit = ((packet_ptr - packet_data) + maxextlen) < packet_length ? ((packet_ptr - packet_data) + maxextlen) : packet_length; -+ -+ //Extension Type and Extension Length are both 2 byte. SNI Extension is "0" -+ while(((packet_ptr - packet_data) < packet_limit) && (ext_type != 0)) -+ { -+ ext_type = ntohs(*(unsigned short*)packet_ptr); -+ packet_ptr = packet_ptr + 2; -+ ext_len = ntohs(*(unsigned short*)packet_ptr); -+ packet_ptr = packet_ptr + 2; -+ /*printk("ext_type=%d, ext_len=%d\n",ext_type,ext_len);*/ -+ if(ext_type == 0) -+ { -+ unsigned short snilen; -+ /*printk("FOUND SNI EXT\n");*/ -+ packet_ptr = packet_ptr + 3; //Skip to length of SNI -+ snilen = ntohs(*(unsigned short*)packet_ptr); -+ /*printk("snilen=%d\n",snilen);*/ -+ packet_ptr = packet_ptr + 2; //Skip to beginning of SNI -+ if((((packet_ptr - packet_data) + snilen) < packet_limit) && (snilen > 0)) -+ { -+ /*printk("FOUND SNI\n");*/ -+ snilen = snilen < 625 ? snilen : 624; // prevent overflow -+ memcpy(domain, packet_ptr, snilen); -+ domain[snilen] = '\0'; -+ for(x=0; domain[x] != '\0'; x++) -+ { -+ domain[x] = (char)tolower(domain[x]); -+ } -+ /*printk("sni=%s\n",domain);*/ -+ } -+ } -+ else -+ { -+ packet_ptr = packet_ptr + ext_len; -+ } -+ } -+} -+ -+#ifdef CONFIG_PROC_FS -+ -+static void *webmon_proc_start(struct seq_file *seq, loff_t *loff_pos) -+{ -+ static unsigned long counter = 0; -+ -+ /* beginning a new sequence ? */ -+ if ( *loff_pos == 0 ) -+ { -+ /* yes => return a non null value to begin the sequence */ -+ return &counter; -+ } -+ else -+ { -+ /* no => it's the end of the sequence, return end to stop reading */ -+ *loff_pos = 0; -+ return NULL; -+ } -+} -+ -+static void *webmon_proc_next(struct seq_file *seq, void *v, loff_t *pos) -+{ -+ return NULL; -+} -+ -+ -+static void webmon_proc_stop(struct seq_file *seq, void *v) -+{ -+ //don't need to do anything -+} -+ -+ -+static int webmon_proc_domain_show(struct seq_file *s, void *v) -+{ -+ queue_node* next_node; -+ spin_lock_bh(&webmon_lock); -+ -+ next_node = recent_domains->last; -+ while(next_node != NULL) -+ { -+ seq_printf(s, "%ld\t"STRIP"\t%s\n", (unsigned long)(next_node->time).tv_sec, NIPQUAD(next_node->src_ip), next_node->value); -+ next_node = (queue_node*)next_node->previous; -+ } -+ spin_unlock_bh(&webmon_lock); -+ -+ return 0; -+} -+ -+static int webmon_proc_search_show(struct seq_file *s, void *v) -+{ -+ queue_node* next_node; -+ spin_lock_bh(&webmon_lock); -+ -+ next_node = recent_searches->last; -+ while(next_node != NULL) -+ { -+ seq_printf(s, "%ld\t"STRIP"\t%s\n", (unsigned long)(next_node->time).tv_sec, NIPQUAD(next_node->src_ip), next_node->value); -+ next_node = (queue_node*)next_node->previous; -+ } -+ spin_unlock_bh(&webmon_lock); -+ -+ return 0; -+} -+ -+ -+static struct seq_operations webmon_proc_domain_sops = { -+ .start = webmon_proc_start, -+ .next = webmon_proc_next, -+ .stop = webmon_proc_stop, -+ .show = webmon_proc_domain_show -+}; -+ -+static struct seq_operations webmon_proc_search_sops = { -+ .start = webmon_proc_start, -+ .next = webmon_proc_next, -+ .stop = webmon_proc_stop, -+ .show = webmon_proc_search_show -+}; -+ -+ -+static int webmon_proc_domain_open(struct inode *inode, struct file* file) -+{ -+ return seq_open(file, &webmon_proc_domain_sops); -+} -+static int webmon_proc_search_open(struct inode *inode, struct file* file) -+{ -+ return seq_open(file, &webmon_proc_search_sops); -+} -+ -+ -+ -+static struct file_operations webmon_proc_domain_fops = { -+ .owner = THIS_MODULE, -+ .open = webmon_proc_domain_open, -+ .read = seq_read, -+ .llseek = seq_lseek, -+ .release = seq_release -+}; -+static struct file_operations webmon_proc_search_fops = { -+ .owner = THIS_MODULE, -+ .open = webmon_proc_search_open, -+ .read = seq_read, -+ .llseek = seq_lseek, -+ .release = seq_release -+}; -+ -+ -+#endif -+ -+ -+ -+ -+ -+ -+static int ipt_webmon_set_ctl(struct sock *sk, int cmd, void *user, u_int32_t len) -+{ -+ -+ char* buffer = kmalloc(len, GFP_ATOMIC); -+ if(buffer == NULL) /* check for malloc failure */ -+ { -+ return 0; -+ } -+ spin_lock_bh(&webmon_lock); -+ copy_from_user(buffer, user, len); -+ -+ if(len > 1 + sizeof(uint32_t)) -+ { -+ unsigned char type = buffer[0]; -+ uint32_t max_queue_length = *((uint32_t*)(buffer+1)); -+ char* data = buffer+1+sizeof(uint32_t); -+ char newline_terminator[] = { '\n', '\r' }; -+ char whitespace_chars[] = { '\t', ' ' }; -+ -+ if(type == WEBMON_DOMAIN || type == WEBMON_SEARCH ) -+ { -+ unsigned long num_destroyed; -+ -+ -+ /* destroy and re-initialize queue and map */ -+ if(type == WEBMON_DOMAIN ) -+ { -+ destroy_map(domain_map, DESTROY_MODE_IGNORE_VALUES, &num_destroyed); -+ destroy_queue(recent_domains); -+ recent_domains = (queue*)malloc(sizeof(queue)); -+ recent_domains->first = NULL; -+ recent_domains->last = NULL; -+ recent_domains->length = 0; -+ domain_map = initialize_map(0); -+ -+ max_domain_queue_length = max_queue_length; -+ } -+ else if(type == WEBMON_SEARCH) -+ { -+ destroy_map(search_map, DESTROY_MODE_IGNORE_VALUES, &num_destroyed); -+ destroy_queue(recent_searches); -+ recent_searches = (queue*)malloc(sizeof(queue)); -+ recent_searches->first = NULL; -+ recent_searches->last = NULL; -+ recent_searches->length = 0; -+ search_map = initialize_map(0); -+ -+ max_search_queue_length = max_queue_length; -+ } -+ -+ if(data[0] != '\0') -+ { -+ unsigned long num_lines; -+ unsigned long line_index; -+ char** lines = split_on_separators(data, newline_terminator, 2, -1, 0, &num_lines); -+ for(line_index=0; line_index < num_lines; line_index++) -+ { -+ char* line = lines[line_index]; -+ unsigned long num_pieces; -+ char** split = split_on_separators(line, whitespace_chars, 2, -1, 0, &num_pieces); -+ -+ //check that there are 3 pieces (time, src_ip, value) -+ int length; -+ for(length=0; split[length] != NULL ; length++){} -+ if(length == 3) -+ { -+ time_t time; -+ int parsed_ip[4]; -+ int valid_ip = sscanf(split[1], "%d.%d.%d.%d", parsed_ip, parsed_ip+1, parsed_ip+2, parsed_ip+3); -+ if(valid_ip == 4) -+ { -+ valid_ip = parsed_ip[0] <= 255 && parsed_ip[1] <= 255 && parsed_ip[2] <= 255 && parsed_ip[3] <= 255 ? valid_ip : 0; -+ } -+ if(sscanf(split[0], "%ld", &time) > 0 && valid_ip == 4) -+ { -+ char* value = split[2]; -+ char value_key[700]; -+ uint32_t ip = (parsed_ip[0]<<24) + (parsed_ip[1]<<16) + (parsed_ip[2]<<8) + (parsed_ip[3]) ; -+ ip = htonl(ip); -+ sprintf(value_key, STRIP"@%s", NIPQUAD(ip), value); -+ if(type == WEBMON_DOMAIN) -+ { -+ add_queue_node(ip, value, recent_domains, domain_map, value_key, max_domain_queue_length ); -+ (recent_domains->first->time).tv_sec = time; -+ } -+ else if(type == WEBMON_SEARCH) -+ { -+ add_queue_node(ip, value, recent_searches, search_map, value_key, max_search_queue_length ); -+ (recent_searches->first->time).tv_sec = time; -+ } -+ } -+ } -+ -+ for(length=0; split[length] != NULL ; length++) -+ { -+ free(split[length]); -+ } -+ free(split); -+ free(line); -+ } -+ free(lines); -+ } -+ } -+ } -+ kfree(buffer); -+ spin_unlock_bh(&webmon_lock); -+ -+ -+ return 1; -+} -+static struct nf_sockopt_ops ipt_webmon_sockopts = -+{ -+ .pf = PF_INET, -+ .set_optmin = WEBMON_SET, -+ .set_optmax = WEBMON_SET+1, -+ .set = ipt_webmon_set_ctl, -+}; -+ -+ -+ -+ -+static bool match(const struct sk_buff *skb, struct xt_action_param *par) -+{ -+ -+ const struct ipt_webmon_info *info = (const struct ipt_webmon_info*)(par->matchinfo); -+ -+ -+ struct iphdr* iph; -+ -+ /* linearize skb if necessary */ -+ struct sk_buff *linear_skb; -+ int skb_copied; -+ if(skb_is_nonlinear(skb)) -+ { -+ linear_skb = skb_copy(skb, GFP_ATOMIC); -+ skb_copied = 1; -+ } -+ else -+ { -+ linear_skb = (struct sk_buff*)skb; -+ skb_copied = 0; -+ } -+ -+ -+ -+ /* ignore packets that are not TCP */ -+ iph = (struct iphdr*)(skb_network_header(skb)); -+ if(iph->protocol == IPPROTO_TCP) -+ { -+ /* get payload */ -+ struct tcphdr* tcp_hdr = (struct tcphdr*)( ((unsigned char*)iph) + (iph->ihl*4) ); -+ unsigned short payload_offset = (tcp_hdr->doff*4) + (iph->ihl*4); -+ unsigned char* payload = ((unsigned char*)iph) + payload_offset; -+ unsigned short payload_length = ntohs(iph->tot_len) - payload_offset; -+ -+ -+ -+ /* if payload length <= 10 bytes don't bother doing a check, otherwise check for match */ -+ if(payload_length > 10) -+ { -+ /* are we dealing with a web page request */ -+ if(strnicmp((char*)payload, "GET ", 4) == 0 || strnicmp( (char*)payload, "POST ", 5) == 0 || strnicmp((char*)payload, "HEAD ", 5) == 0) -+ { -+ char domain[650]; -+ char path[650]; -+ char domain_key[700]; -+ unsigned char save = info->exclude_type == WEBMON_EXCLUDE ? 1 : 0; -+ uint32_t ip_index; -+ -+ -+ for(ip_index = 0; ip_index < info->num_exclude_ips; ip_index++) -+ { -+ if( (info->exclude_ips)[ip_index] == iph->saddr ) -+ { -+ save = info->exclude_type == WEBMON_EXCLUDE ? 0 : 1; -+ } -+ } -+ for(ip_index=0; ip_index < info->num_exclude_ranges; ip_index++) -+ { -+ struct ipt_webmon_ip_range r = (info->exclude_ranges)[ip_index]; -+ if( (unsigned long)ntohl( r.start) <= (unsigned long)ntohl(iph->saddr) && (unsigned long)ntohl(r.end) >= (unsigned long)ntohl(iph->saddr) ) -+ { -+ save = info->exclude_type == WEBMON_EXCLUDE ? 0 : 1; -+ } -+ } -+ -+ -+ if(save) -+ { -+ extract_url(payload, payload_length, domain, path); -+ -+ -+ sprintf(domain_key, STRIP"@%s", NIPQUAD(iph->saddr), domain); -+ -+ if(strlen(domain) > 0) -+ { -+ char *search_part = NULL; -+ spin_lock_bh(&webmon_lock); -+ -+ -+ -+ if(get_string_map_element(domain_map, domain_key)) -+ { -+ //update time -+ update_queue_node_time( (queue_node*)get_map_element(domain_map, domain_key), recent_domains ); -+ } -+ else -+ { -+ //add -+ add_queue_node(iph->saddr, domain, recent_domains, domain_map, domain_key, max_domain_queue_length ); -+ } -+ -+ -+ /* printk("domain,path=\"%s\", \"%s\"\n", domain, path); */ -+ -+ if(strnistr(domain, "google.", 625) != NULL) -+ { -+ search_part = strstr(path, "&q="); -+ search_part = search_part == NULL ? strstr(path, "#q=") : search_part; -+ search_part = search_part == NULL ? strstr(path, "?q=") : search_part; -+ search_part = search_part == NULL ? search_part : search_part+3; -+ } -+ else if(strstr(domain, "bing.") != NULL) -+ { -+ search_part = strstr(path, "?q="); -+ search_part = search_part == NULL ? strstr(path, "&q=") : search_part; -+ search_part = search_part == NULL ? search_part : search_part+3; -+ } -+ else if(strstr(domain, "yahoo.") != NULL) -+ { -+ search_part = strstr(path, "?p="); -+ search_part = search_part == NULL ? strstr(path, "&p=") : search_part; -+ search_part = search_part == NULL ? search_part : search_part+3; -+ } -+ else if(strstr(domain, "lycos.") != NULL) -+ { -+ search_part = strstr(path, "&query="); -+ search_part = search_part == NULL ? strstr(path, "?query=") : search_part; -+ search_part = search_part == NULL ? search_part : search_part+7; -+ } -+ else if(strstr(domain, "altavista.") != NULL) -+ { -+ search_part = strstr(path, "&q="); -+ search_part = search_part == NULL ? strstr(path, "?q=") : search_part; -+ search_part = search_part == NULL ? search_part : search_part+3; -+ } -+ else if(strstr(domain, "duckduckgo.") != NULL) -+ { -+ search_part = strstr(path, "?q="); -+ search_part = search_part == NULL ? strstr(path, "&q=") : search_part; -+ search_part = search_part == NULL ? search_part : search_part+3; -+ } -+ else if(strstr(domain, "baidu.") != NULL) -+ { -+ search_part = strstr(path, "?wd="); -+ search_part = search_part == NULL ? strstr(path, "&wd=") : search_part; -+ search_part = search_part == NULL ? search_part : search_part+4; -+ } -+ else if(strstr(domain, "search.") != NULL) -+ { -+ search_part = strstr(path, "?q="); -+ search_part = search_part == NULL ? strstr(path, "&q=") : search_part; -+ search_part = search_part == NULL ? search_part : search_part+3; -+ } -+ else if(strstr(domain, "aol.") != NULL) -+ { -+ search_part = strstr(path, "&q="); -+ search_part = search_part == NULL ? strstr(path, "?q=") : search_part; -+ search_part = search_part == NULL ? search_part : search_part+3; -+ } -+ else if(strstr(domain, "ask.") != NULL) -+ { -+ search_part = strstr(path, "?q="); -+ search_part = search_part == NULL ? strstr(path, "&q=") : search_part; -+ search_part = search_part == NULL ? search_part : search_part+3; -+ } -+ else if(strstr(domain, "yandex.") != NULL) -+ { -+ search_part = strstr(path, "?text="); -+ search_part = search_part == NULL ? strstr(path, "&text=") : search_part; -+ search_part = search_part == NULL ? search_part : search_part+6; -+ } -+ else if(strstr(domain, "naver.") != NULL) -+ { -+ search_part = strstr(path, "&query="); -+ search_part = search_part == NULL ? strstr(path, "?query=") : search_part; -+ search_part = search_part == NULL ? search_part : search_part+7; -+ } -+ else if(strstr(domain, "daum.") != NULL) -+ { -+ search_part = strstr(path, "&q="); -+ search_part = search_part == NULL ? strstr(path, "?q=") : search_part; -+ search_part = search_part == NULL ? search_part : search_part+3; -+ } -+ else if(strstr(domain, "cuil.") != NULL) -+ { -+ search_part = strstr(path, "?q="); -+ search_part = search_part == NULL ? strstr(path, "&q=") : search_part; -+ search_part = search_part == NULL ? search_part : search_part+3; -+ } -+ else if(strstr(domain, "kosmix.") != NULL) -+ { -+ search_part = strstr(path, "/topic/"); -+ search_part = search_part == NULL ? search_part : search_part+7; -+ } -+ else if(strstr(domain, "yebol.") != NULL) -+ { -+ search_part = strstr(path, "?key="); -+ search_part = search_part == NULL ? strstr(path, "&key=") : search_part; -+ search_part = search_part == NULL ? search_part : search_part+5; -+ } -+ else if(strstr(domain, "sogou.") != NULL) -+ { -+ search_part = strstr(path, "&query="); -+ search_part = search_part == NULL ? strstr(path, "?query=") : search_part; -+ search_part = search_part == NULL ? search_part : search_part+7; -+ } -+ else if(strstr(domain, "youdao.") != NULL) -+ { -+ search_part = strstr(path, "?q="); -+ search_part = search_part == NULL ? strstr(path, "&q=") : search_part; -+ search_part = search_part == NULL ? search_part : search_part+3; -+ } -+ else if(strstr(domain, "metacrawler.") != NULL) -+ { -+ search_part = strstr(path, "/ws/results/Web/"); -+ search_part = search_part == NULL ? search_part : search_part+16; -+ } -+ else if(strstr(domain, "webcrawler.") != NULL) -+ { -+ search_part = strstr(path, "/ws/results/Web/"); -+ search_part = search_part == NULL ? search_part : search_part+16; -+ } -+ else if(strstr(domain, "thepiratebay.") != NULL) -+ { -+ search_part = strstr(path, "/search/"); -+ search_part = search_part == NULL ? search_part : search_part+8; -+ } -+ -+ -+ if(search_part != NULL) -+ { -+ int spi, si; -+ char search_key[700]; -+ char search[650]; -+ queue_node *recent_node = recent_searches->first; -+ -+ /*unescape, replacing whitespace with + */ -+ si = 0; -+ for(spi=0; search_part[spi] != '\0' && search_part[spi] != '&' && search_part[spi] != '/'; spi++) -+ { -+ int parsed_hex = 0; -+ if( search_part[spi] == '%') -+ { -+ if(search_part[spi+1] != '\0' && search_part[spi+1] != '&' && search_part[spi+1] != '/') -+ { -+ if(search_part[spi+2] != '\0' && search_part[spi+2] != '&' && search_part[spi+2] != '/') -+ { -+ char enc[3]; -+ int hex; -+ enc[0] = search_part[spi+1]; -+ enc[1] = search_part[spi+2]; -+ enc[2] = '\0'; -+ if(sscanf(enc, "%x", &hex) > 0) -+ { -+ parsed_hex = 1; -+ search[si] = hex == ' ' || hex == '\t' || hex == '\r' || hex == '\n' ? '+' : (char)hex; -+ spi = spi+2; -+ } -+ } -+ } -+ } -+ if(parsed_hex == 0) -+ { -+ search[si] = search_part[spi]; -+ } -+ si++; -+ } -+ search[si] = '\0'; -+ -+ -+ -+ sprintf(search_key, STRIP"@%s", NIPQUAD(iph->saddr), search); -+ -+ -+ /* Often times search engines will initiate a search as you type it in, but these intermediate queries aren't the real search query -+ * So, if the most recent query is a substring of the current one, discard it in favor of this one -+ */ -+ if(recent_node != NULL) -+ { -+ if(recent_node->src_ip == iph->saddr) -+ { -+ struct timeval t; -+ do_gettimeofday(&t); -+ if( (recent_node->time).tv_sec + 1 >= t.tv_sec || ((recent_node->time).tv_sec + 5 >= t.tv_sec && within_edit_distance(search, recent_node->value, 2))) -+ { -+ char recent_key[700]; -+ -+ sprintf(recent_key, STRIP"@%s", NIPQUAD(recent_node->src_ip), recent_node->value); -+ remove_map_element(search_map, recent_key); -+ -+ recent_searches->first = recent_node->next; -+ recent_searches->last = recent_searches->first == NULL ? NULL : recent_searches->last; -+ if(recent_searches->first != NULL) -+ { -+ recent_searches->first->previous = NULL; -+ } -+ recent_searches->length = recent_searches->length - 1 ; -+ free(recent_node->value); -+ free(recent_node); -+ } -+ } -+ } -+ -+ -+ -+ if(get_string_map_element(search_map, search_key)) -+ { -+ //update time -+ update_queue_node_time( (queue_node*)get_map_element(search_map, search_key), recent_searches ); -+ } -+ else -+ { -+ //add -+ add_queue_node(iph->saddr, search, recent_searches, search_map, search_key, max_search_queue_length ); -+ } -+ } -+ spin_unlock_bh(&webmon_lock); -+ } -+ } -+ } -+ else if ((unsigned short)ntohs(tcp_hdr->dest) == 443) // broad assumption that traffic on 443 is HTTPS. make effort to return fast as soon as we know we are wrong to not slow down processing -+ { -+ char domain[650]; -+ char domain_key[700]; -+ unsigned char save = info->exclude_type == WEBMON_EXCLUDE ? 1 : 0; -+ uint32_t ip_index; -+ -+ for(ip_index = 0; ip_index < info->num_exclude_ips; ip_index++) -+ { -+ if( (info->exclude_ips)[ip_index] == iph->saddr ) -+ { -+ save = info->exclude_type == WEBMON_EXCLUDE ? 0 : 1; -+ } -+ } -+ for(ip_index=0; ip_index < info->num_exclude_ranges; ip_index++) -+ { -+ struct ipt_webmon_ip_range r = (info->exclude_ranges)[ip_index]; -+ if( (unsigned long)ntohl( r.start) <= (unsigned long)ntohl(iph->saddr) && (unsigned long)ntohl(r.end) >= (unsigned long)ntohl(iph->saddr) ) -+ { -+ save = info->exclude_type == WEBMON_EXCLUDE ? 0 : 1; -+ } -+ } -+ -+ -+ if(save) -+ { -+ extract_url_https(payload, payload_length, domain); -+ -+ sprintf(domain_key, STRIP"@%s", NIPQUAD(iph->saddr), domain); -+ -+ if(strlen(domain) > 0) -+ { -+ spin_lock_bh(&webmon_lock); -+ -+ if(get_string_map_element(domain_map, domain_key)) -+ { -+ //update time -+ update_queue_node_time( (queue_node*)get_map_element(domain_map, domain_key), recent_domains ); -+ } -+ else -+ { -+ //add -+ add_queue_node(iph->saddr, domain, recent_domains, domain_map, domain_key, max_domain_queue_length ); -+ } -+ -+ spin_unlock_bh(&webmon_lock); -+ } -+ } -+ } -+ } -+ } -+ -+ /* free skb if we made a copy to linearize it */ -+ if(skb_copied == 1) -+ { -+ kfree_skb(linear_skb); -+ } -+ -+ -+ /* printk("returning %d from webmon\n\n\n", test); */ -+ return 0; -+} -+ -+ -+ -+static int checkentry(const struct xt_mtchk_param *par) -+{ -+ -+ struct ipt_webmon_info *info = (struct ipt_webmon_info*)(par->matchinfo); -+ -+ -+ spin_lock_bh(&webmon_lock); -+ if(info->ref_count == NULL) /* first instance, we're inserting rule */ -+ { -+ info->ref_count = (uint32_t*)kmalloc(sizeof(uint32_t), GFP_ATOMIC); -+ if(info->ref_count == NULL) /* deal with kmalloc failure */ -+ { -+ printk("ipt_webmon: kmalloc failure in checkentry!\n"); -+ return 0; -+ } -+ *(info->ref_count) = 1; -+ -+ -+ max_search_queue_length = info->max_searches; -+ max_domain_queue_length = info->max_domains; -+ -+ -+ } -+ else -+ { -+ *(info->ref_count) = *(info->ref_count) + 1; -+ } -+ spin_unlock_bh(&webmon_lock); -+ -+ return 0; -+} -+ -+static void destroy( const struct xt_mtdtor_param *par ) -+{ -+ struct ipt_webmon_info *info = (struct ipt_webmon_info*)(par->matchinfo); -+ -+ spin_lock_bh(&webmon_lock); -+ *(info->ref_count) = *(info->ref_count) - 1; -+ if(*(info->ref_count) == 0) -+ { -+ kfree(info->ref_count); -+ } -+ spin_unlock_bh(&webmon_lock); -+ -+} -+ -+static struct xt_match webmon_match __read_mostly = -+{ -+ -+ .name = "webmon", -+ .match = match, -+ .family = AF_INET, -+ .matchsize = sizeof(struct ipt_webmon_info), -+ .checkentry = checkentry, -+ .destroy = destroy, -+ .me = THIS_MODULE, -+}; -+ -+static int __init init(void) -+{ -+ -+ #ifdef CONFIG_PROC_FS -+ struct proc_dir_entry *proc_webmon_recent_domains; -+ struct proc_dir_entry *proc_webmon_recent_searches; -+ #endif -+ -+ spin_lock_bh(&webmon_lock); -+ -+ recent_domains = (queue*)malloc(sizeof(queue)); -+ recent_domains->first = NULL; -+ recent_domains->last = NULL; -+ recent_domains->length = 0; -+ domain_map = initialize_string_map(0); -+ -+ recent_searches = (queue*)malloc(sizeof(queue)); -+ recent_searches->first = NULL; -+ recent_searches->last = NULL; -+ recent_searches->length = 0; -+ search_map = initialize_string_map(0); -+ -+ -+ -+ #ifdef CONFIG_PROC_FS -+ proc_create("webmon_recent_domains", 0, NULL, &webmon_proc_domain_fops); -+ proc_create("webmon_recent_searches", 0, NULL, &webmon_proc_search_fops); -+ #endif -+ -+ if (nf_register_sockopt(&ipt_webmon_sockopts) < 0) -+ { -+ printk("ipt_webmon: Can't register sockopts. Aborting\n"); -+ spin_unlock_bh(&webmon_lock); -+ return -1; -+ } -+ spin_unlock_bh(&webmon_lock); -+ -+ return xt_register_match(&webmon_match); -+} -+ -+static void __exit fini(void) -+{ -+ -+ unsigned long num_destroyed; -+ -+ spin_lock_bh(&webmon_lock); -+ -+ -+ #ifdef CONFIG_PROC_FS -+ remove_proc_entry("webmon_recent_domains", NULL); -+ remove_proc_entry("webmon_recent_searches", NULL); -+ #endif -+ nf_unregister_sockopt(&ipt_webmon_sockopts); -+ xt_unregister_match(&webmon_match); -+ destroy_map(domain_map, DESTROY_MODE_IGNORE_VALUES, &num_destroyed); -+ destroy_map(search_map, DESTROY_MODE_IGNORE_VALUES, &num_destroyed); -+ destroy_queue(recent_domains); -+ destroy_queue(recent_searches); -+ -+ spin_unlock_bh(&webmon_lock); -+ -+ -+} -+ -+module_init(init); -+module_exit(fini); - ---- /dev/null -+++ b/net/ipv4/netfilter/ipt_weburl.c -@@ -0,0 +1,537 @@ -+/* weburl -- A netfilter module to match URLs in HTTP(S) requests -+ * This module can match using string match or regular expressions -+ * Originally designed for use with Gargoyle router firmware (gargoyle-router.com) -+ * -+ * -+ * Copyright © 2008-2010 by Eric Bishop -+ * -+ * This file is free software: you may copy, redistribute and/or modify it -+ * under the terms of the GNU General Public License as published by the -+ * Free Software Foundation, either version 2 of the License, or (at your -+ * option) any later version. -+ * -+ * This file is distributed in the hope that it will be useful, but -+ * WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#include "weburl_deps/regexp.c" -+#include "weburl_deps/tree_map.h" -+ -+ -+#include -+ -+ -+#include -+ -+ -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Eric Bishop"); -+MODULE_DESCRIPTION("Match URL in HTTP(S) requests, designed for use with Gargoyle web interface (www.gargoyle-router.com)"); -+ -+string_map* compiled_map = NULL; -+ -+int strnicmp(const char * cs,const char * ct,size_t count) -+{ -+ register signed char __res = 0; -+ -+ while (count) -+ { -+ if ((__res = toupper( *cs ) - toupper( *ct++ ) ) != 0 || !*cs++) -+ { -+ break; -+ } -+ count--; -+ } -+ return __res; -+} -+ -+char *strnistr(const char *s, const char *find, size_t slen) -+{ -+ char c, sc; -+ size_t len; -+ -+ -+ if ((c = *find++) != '\0') -+ { -+ len = strlen(find); -+ do -+ { -+ do -+ { -+ if (slen < 1 || (sc = *s) == '\0') -+ { -+ return (NULL); -+ } -+ --slen; -+ ++s; -+ } -+ while ( toupper(sc) != toupper(c)); -+ -+ if (len > slen) -+ { -+ return (NULL); -+ } -+ } -+ while (strnicmp(s, find, len) != 0); -+ -+ s--; -+ } -+ return ((char *)s); -+} -+ -+ -+int do_match_test(unsigned char match_type, const char* reference, char* query) -+{ -+ int matches = 0; -+ struct regexp* r; -+ switch(match_type) -+ { -+ case WEBURL_CONTAINS_TYPE: -+ matches = (strstr(query, reference) != NULL); -+ break; -+ case WEBURL_REGEX_TYPE: -+ -+ if(compiled_map == NULL) -+ { -+ compiled_map = initialize_map(0); -+ if(compiled_map == NULL) /* test for malloc failure */ -+ { -+ return 0; -+ } -+ } -+ r = (struct regexp*)get_map_element(compiled_map, reference); -+ if(r == NULL) -+ { -+ int rlen = strlen(reference); -+ r= regcomp((char*)reference, &rlen); -+ if(r == NULL) /* test for malloc failure */ -+ { -+ return 0; -+ } -+ set_map_element(compiled_map, reference, (void*)r); -+ } -+ matches = regexec(r, query); -+ break; -+ case WEBURL_EXACT_TYPE: -+ matches = (strstr(query, reference) != NULL) && strlen(query) == strlen(reference); -+ break; -+ } -+ return matches; -+} -+ -+int http_match(const struct ipt_weburl_info* info, const unsigned char* packet_data, int packet_length) -+{ -+ int test = 0; -+ -+ /* printk("found a http web page request\n"); */ -+ char path[625] = ""; -+ char host[625] = ""; -+ int path_start_index; -+ int path_end_index; -+ int last_header_index; -+ char last_two_buf[2]; -+ int end_found; -+ char* host_match; -+ char* test_prefixes[6]; -+ int prefix_index; -+ -+ /* get path portion of URL */ -+ path_start_index = (int)(strstr((char*)packet_data, " ") - (char*)packet_data); -+ while( packet_data[path_start_index] == ' ') -+ { -+ path_start_index++; -+ } -+ path_end_index= (int)(strstr( (char*)(packet_data+path_start_index), " ") - (char*)packet_data); -+ if(path_end_index > 0) -+ { -+ int path_length = path_end_index-path_start_index; -+ path_length = path_length < 625 ? path_length : 624; /* prevent overflow */ -+ memcpy(path, packet_data+path_start_index, path_length); -+ path[ path_length] = '\0'; -+ } -+ -+ /* get header length */ -+ last_header_index = 2; -+ memcpy(last_two_buf,(char*)packet_data, 2); -+ end_found = 0; -+ while(end_found == 0 && last_header_index < packet_length) -+ { -+ char next = (char)packet_data[last_header_index]; -+ if(next == '\n') -+ { -+ end_found = last_two_buf[1] == '\n' || (last_two_buf[0] == '\n' && last_two_buf[1] == '\r') ? 1 : 0; -+ } -+ if(end_found == 0) -+ { -+ last_two_buf[0] = last_two_buf[1]; -+ last_two_buf[1] = next; -+ last_header_index++; -+ } -+ } -+ -+ /* get host portion of URL */ -+ host_match = strnistr( (char*)packet_data, "Host:", last_header_index); -+ if(host_match != NULL) -+ { -+ int host_end_index; -+ host_match = host_match + 5; /* character after "Host:" */ -+ while(host_match[0] == ' ') -+ { -+ host_match = host_match+1; -+ } -+ -+ host_end_index = 0; -+ while( host_match[host_end_index] != '\n' && -+ host_match[host_end_index] != '\r' && -+ host_match[host_end_index] != ' ' && -+ host_match[host_end_index] != ':' && -+ ((char*)host_match - (char*)packet_data)+host_end_index < last_header_index -+ ) -+ { -+ host_end_index++; -+ } -+ memcpy(host, host_match, host_end_index); -+ host_end_index = host_end_index < 625 ? host_end_index : 624; /* prevent overflow */ -+ host[host_end_index] = '\0'; -+ -+ -+ } -+ -+ /* printk("host = \"%s\", path =\"%s\"\n", host, path); */ -+ -+ -+ switch(info->match_part) -+ { -+ case WEBURL_DOMAIN_PART: -+ test = do_match_test(info->match_type, info->test_str, host); -+ if(!test && strstr(host, "www.") == host) -+ { -+ test = do_match_test(info->match_type, info->test_str, ((char*)host+4) ); -+ } -+ break; -+ case WEBURL_PATH_PART: -+ test = do_match_test(info->match_type, info->test_str, path); -+ if( !test && path[0] == '/' ) -+ { -+ test = do_match_test(info->match_type, info->test_str, ((char*)path+1) ); -+ } -+ break; -+ case WEBURL_ALL_PART: -+ -+ test_prefixes[0] = "http://"; -+ test_prefixes[1] = ""; -+ test_prefixes[2] = NULL; -+ -+ -+ for(prefix_index=0; test_prefixes[prefix_index] != NULL && test == 0; prefix_index++) -+ { -+ char test_url[1250]; -+ test_url[0] = '\0'; -+ strcat(test_url, test_prefixes[prefix_index]); -+ strcat(test_url, host); -+ if(strcmp(path, "/") != 0) -+ { -+ strcat(test_url, path); -+ } -+ test = do_match_test(info->match_type, info->test_str, test_url); -+ if(!test && strcmp(path, "/") == 0) -+ { -+ strcat(test_url, path); -+ test = do_match_test(info->match_type, info->test_str, test_url); -+ } -+ -+ /* printk("test_url = \"%s\", test=%d\n", test_url, test); */ -+ } -+ if(!test && strstr(host, "www.") == host) -+ { -+ char* www_host = ((char*)host+4); -+ for(prefix_index=0; test_prefixes[prefix_index] != NULL && test == 0; prefix_index++) -+ { -+ char test_url[1250]; -+ test_url[0] = '\0'; -+ strcat(test_url, test_prefixes[prefix_index]); -+ strcat(test_url, www_host); -+ if(strcmp(path, "/") != 0) -+ { -+ strcat(test_url, path); -+ } -+ test = do_match_test(info->match_type, info->test_str, test_url); -+ if(!test && strcmp(path, "/") == 0) -+ { -+ strcat(test_url, path); -+ test = do_match_test(info->match_type, info->test_str, test_url); -+ } -+ -+ /* printk("test_url = \"%s\", test=%d\n", test_url, test); */ -+ } -+ } -+ break; -+ -+ } -+ -+ -+ /* -+ * If invert flag is set, return true if it didn't match -+ */ -+ test = info->invert ? !test : test; -+ -+ return test; -+} -+ -+int https_match(const struct ipt_weburl_info* info, const unsigned char* packet_data, int packet_length) -+{ -+ int test = 0; -+ -+ /* printk("found a https web page request\n"); */ -+ char host[625] = ""; -+ char* test_prefixes[6]; -+ int prefix_index, x, packet_limit; -+ unsigned short cslen, ext_type, ext_len, maxextlen; -+ unsigned char conttype, hndshktype, sidlen, cmplen; -+ unsigned char* packet_ptr; -+ -+ host[0] = '\0'; -+ packet_ptr = packet_data; -+ -+ if (packet_length < 43) -+ { -+ /*printk("Packet less than 43 bytes, exiting\n");*/ -+ return test; -+ } -+ conttype = packet_data[0]; -+ hndshktype = packet_data[5]; -+ sidlen = packet_data[43]; -+ /*printk("conttype=%d, hndshktype=%d, sidlen=%d ",conttype,hndshktype,sidlen);*/ -+ if(conttype != 22) -+ { -+ /*printk("conttype not 22, exiting\n");*/ -+ return test; -+ } -+ if(hndshktype != 1) -+ { -+ /*printk("hndshktype not 1, exiting\n");*/ -+ return test; //We aren't in a Client Hello -+ } -+ -+ packet_ptr = packet_data + 1 + 43 + sidlen; //Skip to Cipher Suites Length -+ cslen = ntohs(*(unsigned short*)packet_ptr); //Length of Cipher Suites (2 byte) -+ packet_ptr = packet_ptr + 2 + cslen; //Skip to Compression Methods -+ cmplen = *packet_ptr; //Length of Compression Methods (1 byte) -+ packet_ptr = packet_ptr + 1 + cmplen; //Skip to Extensions Length **IMPORTANT** -+ maxextlen = ntohs(*(unsigned short*)packet_ptr); //Length of extensions (2 byte) -+ packet_ptr = packet_ptr + 2; //Skip to beginning of first extension and start looping -+ ext_type = 1; -+ /*printk("cslen=%d, cmplen=%d, maxextlen=%d, pktlen=%d,ptrpos=%d\n",cslen,cmplen,maxextlen,packet_length,packet_ptr - packet_data);*/ -+ //Limit the pointer bounds to the smaller of either the extensions length or the packet length -+ packet_limit = ((packet_ptr - packet_data) + maxextlen) < packet_length ? ((packet_ptr - packet_data) + maxextlen) : packet_length; -+ -+ //Extension Type and Extension Length are both 2 byte. SNI Extension is "0" -+ while(((packet_ptr - packet_data) < packet_limit) && (ext_type != 0)) -+ { -+ ext_type = ntohs(*(unsigned short*)packet_ptr); -+ packet_ptr = packet_ptr + 2; -+ ext_len = ntohs(*(unsigned short*)packet_ptr); -+ packet_ptr = packet_ptr + 2; -+ /*printk("ext_type=%d, ext_len=%d\n",ext_type,ext_len);*/ -+ if(ext_type == 0) -+ { -+ unsigned short snilen; -+ /*printk("FOUND SNI EXT\n");*/ -+ packet_ptr = packet_ptr + 3; //Skip to length of SNI -+ snilen = ntohs(*(unsigned short*)packet_ptr); -+ /*printk("snilen=%d\n",snilen);*/ -+ packet_ptr = packet_ptr + 2; //Skip to beginning of SNI -+ if((((packet_ptr - packet_data) + snilen) < packet_limit) && (snilen > 0)) -+ { -+ /*printk("FOUND SNI\n");*/ -+ snilen = snilen < 625 ? snilen : 624; // prevent overflow -+ memcpy(host, packet_ptr, snilen); -+ host[snilen] = '\0'; -+ for(x=0; host[x] != '\0'; x++) -+ { -+ host[x] = (char)tolower(host[x]); -+ } -+ /*printk("sni=%s\n",host);*/ -+ } -+ } -+ else -+ { -+ packet_ptr = packet_ptr + ext_len; -+ } -+ } -+ -+ /* printk("host = \"%s\"\n", host); */ -+ -+ switch(info->match_part) -+ { -+ case WEBURL_DOMAIN_PART: -+ test = do_match_test(info->match_type, info->test_str, host); -+ if(!test && strstr(host, "www.") == host) -+ { -+ test = do_match_test(info->match_type, info->test_str, ((char*)host+4) ); -+ } -+ break; -+ case WEBURL_PATH_PART: -+ test = 0; //we will never have a Path for HTTPS -+ break; -+ case WEBURL_ALL_PART: -+ test_prefixes[0] = "https://"; -+ test_prefixes[1] = ""; -+ test_prefixes[2] = NULL; -+ -+ for(prefix_index=0; test_prefixes[prefix_index] != NULL && test == 0; prefix_index++) -+ { -+ char test_url[1250]; -+ test_url[0] = '\0'; -+ strcat(test_url, test_prefixes[prefix_index]); -+ strcat(test_url, host); -+ -+ test = do_match_test(info->match_type, info->test_str, test_url); -+ -+ /* printk("test_url = \"%s\", test=%d\n", test_url, test); */ -+ } -+ if(!test && strstr(host, "www.") == host) -+ { -+ char* www_host = ((char*)host+4); -+ for(prefix_index=0; test_prefixes[prefix_index] != NULL && test == 0; prefix_index++) -+ { -+ char test_url[1250]; -+ test_url[0] = '\0'; -+ strcat(test_url, test_prefixes[prefix_index]); -+ strcat(test_url, www_host); -+ -+ test = do_match_test(info->match_type, info->test_str, test_url); -+ -+ /* printk("test_url = \"%s\", test=%d\n", test_url, test); */ -+ } -+ } -+ break; -+ } -+ -+ /* -+ * If invert flag is set, return true if it didn't match -+ */ -+ test = info->invert ? !test : test; -+ -+ return test; -+} -+ -+ -+static bool match(const struct sk_buff *skb, struct xt_action_param *par) -+{ -+ -+ const struct ipt_weburl_info *info = (const struct ipt_weburl_info*)(par->matchinfo); -+ -+ -+ int test = 0; -+ struct iphdr* iph; -+ -+ /* linearize skb if necessary */ -+ struct sk_buff *linear_skb; -+ int skb_copied; -+ if(skb_is_nonlinear(skb)) -+ { -+ linear_skb = skb_copy(skb, GFP_ATOMIC); -+ skb_copied = 1; -+ } -+ else -+ { -+ linear_skb = (struct sk_buff*)skb; -+ skb_copied = 0; -+ } -+ -+ -+ -+ /* ignore packets that are not TCP */ -+ iph = (struct iphdr*)(skb_network_header(skb)); -+ if(iph->protocol == IPPROTO_TCP) -+ { -+ /* get payload */ -+ struct tcphdr* tcp_hdr = (struct tcphdr*)( ((unsigned char*)iph) + (iph->ihl*4) ); -+ unsigned short payload_offset = (tcp_hdr->doff*4) + (iph->ihl*4); -+ unsigned char* payload = ((unsigned char*)iph) + payload_offset; -+ unsigned short payload_length = ntohs(iph->tot_len) - payload_offset; -+ -+ -+ -+ /* if payload length <= 10 bytes don't bother doing a check, otherwise check for match */ -+ if(payload_length > 10) -+ { -+ if(strnicmp((char*)payload, "GET ", 4) == 0 || strnicmp( (char*)payload, "POST ", 5) == 0 || strnicmp((char*)payload, "HEAD ", 5) == 0) -+ { -+ test = http_match(info, payload, payload_length); -+ } -+ else if ((unsigned short)ntohs(tcp_hdr->dest) == 443) -+ { -+ test = https_match(info, payload, payload_length); -+ } -+ } -+ } -+ -+ /* free skb if we made a copy to linearize it */ -+ if(skb_copied == 1) -+ { -+ kfree_skb(linear_skb); -+ } -+ -+ -+ /* printk("returning %d from weburl\n\n\n", test); */ -+ return test; -+} -+ -+ -+static int checkentry(const struct xt_mtchk_param *par) -+{ -+ return 0; -+} -+ -+ -+static struct xt_match weburl_match __read_mostly = -+{ -+ .name = "weburl", -+ .match = match, -+ .family = AF_INET, -+ .matchsize = sizeof(struct ipt_weburl_info), -+ .checkentry = checkentry, -+ .me = THIS_MODULE, -+}; -+ -+static int __init init(void) -+{ -+ compiled_map = NULL; -+ return xt_register_match(&weburl_match); -+ -+} -+ -+static void __exit fini(void) -+{ -+ xt_unregister_match(&weburl_match); -+ if(compiled_map != NULL) -+ { -+ unsigned long num_destroyed; -+ destroy_map(compiled_map, DESTROY_MODE_FREE_VALUES, &num_destroyed); -+ } -+} -+ -+module_init(init); -+module_exit(fini); - ---- /dev/null -+++ b/net/ipv4/netfilter/ipt_weburl.mod.c -@@ -0,0 +1,20 @@ -+#include -+#include -+#include -+ -+MODULE_INFO(vermagic, VERMAGIC_STRING); -+ -+struct module __this_module -+__attribute__((section(".gnu.linkonce.this_module"))) = { -+ .name = KBUILD_MODNAME, -+ .init = init_module, -+#ifdef CONFIG_MODULE_UNLOAD -+ .exit = cleanup_module, -+#endif -+}; -+ -+static const char __module_depends[] -+__attribute_used__ -+__attribute__((section(".modinfo"))) = -+"depends="; -+ ---- /dev/null -+++ b/net/ipv4/netfilter/regexp/regexp.c -@@ -0,0 +1,1197 @@ -+/* -+ * regcomp and regexec -- regsub and regerror are elsewhere -+ * @(#)regexp.c 1.3 of 18 April 87 -+ * -+ * Copyright (c) 1986 by University of Toronto. -+ * Written by Henry Spencer. Not derived from licensed software. -+ * -+ * Permission is granted to anyone to use this software for any -+ * purpose on any computer system, and to redistribute it freely, -+ * subject to the following restrictions: -+ * -+ * 1. The author is not responsible for the consequences of use of -+ * this software, no matter how awful, even if they arise -+ * from defects in it. -+ * -+ * 2. The origin of this software must not be misrepresented, either -+ * by explicit claim or by omission. -+ * -+ * 3. Altered versions must be plainly marked as such, and must not -+ * be misrepresented as being the original software. -+ * -+ * Beware that some of this code is subtly aware of the way operator -+ * precedence is structured in regular expressions. Serious changes in -+ * regular-expression syntax might require a total rethink. -+ * -+ * This code was modified by Ethan Sommer to work within the kernel -+ * (it now uses kmalloc etc..) -+ * -+ * Modified slightly by Matthew Strait to use more modern C. -+ */ -+ -+#include "regexp.h" -+#include "regmagic.h" -+ -+/* added by ethan and matt. Lets it work in both kernel and user space. -+(So iptables can use it, for instance.) Yea, it goes both ways... */ -+#if __KERNEL__ -+ #define malloc(foo) kmalloc(foo,GFP_ATOMIC) -+#else -+ #define printk(format,args...) printf(format,##args) -+#endif -+ -+void regerror(char * s) -+{ -+ printk("<3>Regexp: %s\n", s); -+ /* NOTREACHED */ -+} -+ -+/* -+ * The "internal use only" fields in regexp.h are present to pass info from -+ * compile to execute that permits the execute phase to run lots faster on -+ * simple cases. They are: -+ * -+ * regstart char that must begin a match; '\0' if none obvious -+ * reganch is the match anchored (at beginning-of-line only)? -+ * regmust string (pointer into program) that match must include, or NULL -+ * regmlen length of regmust string -+ * -+ * Regstart and reganch permit very fast decisions on suitable starting points -+ * for a match, cutting down the work a lot. Regmust permits fast rejection -+ * of lines that cannot possibly match. The regmust tests are costly enough -+ * that regcomp() supplies a regmust only if the r.e. contains something -+ * potentially expensive (at present, the only such thing detected is * or + -+ * at the start of the r.e., which can involve a lot of backup). Regmlen is -+ * supplied because the test in regexec() needs it and regcomp() is computing -+ * it anyway. -+ */ -+ -+/* -+ * Structure for regexp "program". This is essentially a linear encoding -+ * of a nondeterministic finite-state machine (aka syntax charts or -+ * "railroad normal form" in parsing technology). Each node is an opcode -+ * plus a "next" pointer, possibly plus an operand. "Next" pointers of -+ * all nodes except BRANCH implement concatenation; a "next" pointer with -+ * a BRANCH on both ends of it is connecting two alternatives. (Here we -+ * have one of the subtle syntax dependencies: an individual BRANCH (as -+ * opposed to a collection of them) is never concatenated with anything -+ * because of operator precedence.) The operand of some types of node is -+ * a literal string; for others, it is a node leading into a sub-FSM. In -+ * particular, the operand of a BRANCH node is the first node of the branch. -+ * (NB this is *not* a tree structure: the tail of the branch connects -+ * to the thing following the set of BRANCHes.) The opcodes are: -+ */ -+ -+/* definition number opnd? meaning */ -+#define END 0 /* no End of program. */ -+#define BOL 1 /* no Match "" at beginning of line. */ -+#define EOL 2 /* no Match "" at end of line. */ -+#define ANY 3 /* no Match any one character. */ -+#define ANYOF 4 /* str Match any character in this string. */ -+#define ANYBUT 5 /* str Match any character not in this string. */ -+#define BRANCH 6 /* node Match this alternative, or the next... */ -+#define BACK 7 /* no Match "", "next" ptr points backward. */ -+#define EXACTLY 8 /* str Match this string. */ -+#define NOTHING 9 /* no Match empty string. */ -+#define STAR 10 /* node Match this (simple) thing 0 or more times. */ -+#define PLUS 11 /* node Match this (simple) thing 1 or more times. */ -+#define OPEN 20 /* no Mark this point in input as start of #n. */ -+ /* OPEN+1 is number 1, etc. */ -+#define CLOSE 30 /* no Analogous to OPEN. */ -+ -+/* -+ * Opcode notes: -+ * -+ * BRANCH The set of branches constituting a single choice are hooked -+ * together with their "next" pointers, since precedence prevents -+ * anything being concatenated to any individual branch. The -+ * "next" pointer of the last BRANCH in a choice points to the -+ * thing following the whole choice. This is also where the -+ * final "next" pointer of each individual branch points; each -+ * branch starts with the operand node of a BRANCH node. -+ * -+ * BACK Normal "next" pointers all implicitly point forward; BACK -+ * exists to make loop structures possible. -+ * -+ * STAR,PLUS '?', and complex '*' and '+', are implemented as circular -+ * BRANCH structures using BACK. Simple cases (one character -+ * per match) are implemented with STAR and PLUS for speed -+ * and to minimize recursive plunges. -+ * -+ * OPEN,CLOSE ...are numbered at compile time. -+ */ -+ -+/* -+ * A node is one char of opcode followed by two chars of "next" pointer. -+ * "Next" pointers are stored as two 8-bit pieces, high order first. The -+ * value is a positive offset from the opcode of the node containing it. -+ * An operand, if any, simply follows the node. (Note that much of the -+ * code generation knows about this implicit relationship.) -+ * -+ * Using two bytes for the "next" pointer is vast overkill for most things, -+ * but allows patterns to get big without disasters. -+ */ -+#define OP(p) (*(p)) -+#define NEXT(p) (((*((p)+1)&0377)<<8) + (*((p)+2)&0377)) -+#define OPERAND(p) ((p) + 3) -+ -+/* -+ * See regmagic.h for one further detail of program structure. -+ */ -+ -+ -+/* -+ * Utility definitions. -+ */ -+#ifndef CHARBITS -+#define UCHARAT(p) ((int)*(unsigned char *)(p)) -+#else -+#define UCHARAT(p) ((int)*(p)&CHARBITS) -+#endif -+ -+#define FAIL(m) { regerror(m); return(NULL); } -+#define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?') -+#define META "^$.[()|?+*\\" -+ -+/* -+ * Flags to be passed up and down. -+ */ -+#define HASWIDTH 01 /* Known never to match null string. */ -+#define SIMPLE 02 /* Simple enough to be STAR/PLUS operand. */ -+#define SPSTART 04 /* Starts with * or +. */ -+#define WORST 0 /* Worst case. */ -+ -+/* -+ * Global work variables for regcomp(). -+ */ -+struct match_globals { -+char *reginput; /* String-input pointer. */ -+char *regbol; /* Beginning of input, for ^ check. */ -+char **regstartp; /* Pointer to startp array. */ -+char **regendp; /* Ditto for endp. */ -+char *regparse; /* Input-scan pointer. */ -+int regnpar; /* () count. */ -+char regdummy; -+char *regcode; /* Code-emit pointer; ®dummy = don't. */ -+long regsize; /* Code size. */ -+}; -+ -+/* -+ * Forward declarations for regcomp()'s friends. -+ */ -+#ifndef STATIC -+#define STATIC static -+#endif -+STATIC char *reg(struct match_globals *g, int paren,int *flagp); -+STATIC char *regbranch(struct match_globals *g, int *flagp); -+STATIC char *regpiece(struct match_globals *g, int *flagp); -+STATIC char *regatom(struct match_globals *g, int *flagp); -+STATIC char *regnode(struct match_globals *g, char op); -+STATIC char *regnext(struct match_globals *g, char *p); -+STATIC void regc(struct match_globals *g, char b); -+STATIC void reginsert(struct match_globals *g, char op, char *opnd); -+STATIC void regtail(struct match_globals *g, char *p, char *val); -+STATIC void regoptail(struct match_globals *g, char *p, char *val); -+ -+ -+__kernel_size_t my_strcspn(const char *s1,const char *s2) -+{ -+ char *scan1; -+ char *scan2; -+ int count; -+ -+ count = 0; -+ for (scan1 = (char *)s1; *scan1 != '\0'; scan1++) { -+ for (scan2 = (char *)s2; *scan2 != '\0';) /* ++ moved down. */ -+ if (*scan1 == *scan2++) -+ return(count); -+ count++; -+ } -+ return(count); -+} -+ -+/* -+ - regcomp - compile a regular expression into internal code -+ * -+ * We can't allocate space until we know how big the compiled form will be, -+ * but we can't compile it (and thus know how big it is) until we've got a -+ * place to put the code. So we cheat: we compile it twice, once with code -+ * generation turned off and size counting turned on, and once "for real". -+ * This also means that we don't allocate space until we are sure that the -+ * thing really will compile successfully, and we never have to move the -+ * code and thus invalidate pointers into it. (Note that it has to be in -+ * one piece because free() must be able to free it all.) -+ * -+ * Beware that the optimization-preparation code in here knows about some -+ * of the structure of the compiled regexp. -+ */ -+regexp * -+regcomp(char *exp,int *patternsize) -+{ -+ register regexp *r; -+ register char *scan; -+ register char *longest; -+ register int len; -+ int flags; -+ struct match_globals g; -+ -+ /* commented out by ethan -+ extern char *malloc(); -+ */ -+ -+ if (exp == NULL) -+ FAIL("NULL argument"); -+ -+ /* First pass: determine size, legality. */ -+ g.regparse = exp; -+ g.regnpar = 1; -+ g.regsize = 0L; -+ g.regcode = &g.regdummy; -+ regc(&g, MAGIC); -+ if (reg(&g, 0, &flags) == NULL) -+ return(NULL); -+ -+ /* Small enough for pointer-storage convention? */ -+ if (g.regsize >= 32767L) /* Probably could be 65535L. */ -+ FAIL("regexp too big"); -+ -+ /* Allocate space. */ -+ *patternsize=sizeof(regexp) + (unsigned)g.regsize; -+ r = (regexp *)malloc(sizeof(regexp) + (unsigned)g.regsize); -+ if (r == NULL) -+ FAIL("out of space"); -+ -+ /* Second pass: emit code. */ -+ g.regparse = exp; -+ g.regnpar = 1; -+ g.regcode = r->program; -+ regc(&g, MAGIC); -+ if (reg(&g, 0, &flags) == NULL) -+ return(NULL); -+ -+ /* Dig out information for optimizations. */ -+ r->regstart = '\0'; /* Worst-case defaults. */ -+ r->reganch = 0; -+ r->regmust = NULL; -+ r->regmlen = 0; -+ scan = r->program+1; /* First BRANCH. */ -+ if (OP(regnext(&g, scan)) == END) { /* Only one top-level choice. */ -+ scan = OPERAND(scan); -+ -+ /* Starting-point info. */ -+ if (OP(scan) == EXACTLY) -+ r->regstart = *OPERAND(scan); -+ else if (OP(scan) == BOL) -+ r->reganch++; -+ -+ /* -+ * If there's something expensive in the r.e., find the -+ * longest literal string that must appear and make it the -+ * regmust. Resolve ties in favor of later strings, since -+ * the regstart check works with the beginning of the r.e. -+ * and avoiding duplication strengthens checking. Not a -+ * strong reason, but sufficient in the absence of others. -+ */ -+ if (flags&SPSTART) { -+ longest = NULL; -+ len = 0; -+ for (; scan != NULL; scan = regnext(&g, scan)) -+ if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) { -+ longest = OPERAND(scan); -+ len = strlen(OPERAND(scan)); -+ } -+ r->regmust = longest; -+ r->regmlen = len; -+ } -+ } -+ -+ return(r); -+} -+ -+/* -+ - reg - regular expression, i.e. main body or parenthesized thing -+ * -+ * Caller must absorb opening parenthesis. -+ * -+ * Combining parenthesis handling with the base level of regular expression -+ * is a trifle forced, but the need to tie the tails of the branches to what -+ * follows makes it hard to avoid. -+ */ -+static char * -+reg(struct match_globals *g, int paren, int *flagp /* Parenthesized? */ ) -+{ -+ register char *ret; -+ register char *br; -+ register char *ender; -+ register int parno = 0; /* 0 makes gcc happy */ -+ int flags; -+ -+ *flagp = HASWIDTH; /* Tentatively. */ -+ -+ /* Make an OPEN node, if parenthesized. */ -+ if (paren) { -+ if (g->regnpar >= NSUBEXP) -+ FAIL("too many ()"); -+ parno = g->regnpar; -+ g->regnpar++; -+ ret = regnode(g, OPEN+parno); -+ } else -+ ret = NULL; -+ -+ /* Pick up the branches, linking them together. */ -+ br = regbranch(g, &flags); -+ if (br == NULL) -+ return(NULL); -+ if (ret != NULL) -+ regtail(g, ret, br); /* OPEN -> first. */ -+ else -+ ret = br; -+ if (!(flags&HASWIDTH)) -+ *flagp &= ~HASWIDTH; -+ *flagp |= flags&SPSTART; -+ while (*g->regparse == '|') { -+ g->regparse++; -+ br = regbranch(g, &flags); -+ if (br == NULL) -+ return(NULL); -+ regtail(g, ret, br); /* BRANCH -> BRANCH. */ -+ if (!(flags&HASWIDTH)) -+ *flagp &= ~HASWIDTH; -+ *flagp |= flags&SPSTART; -+ } -+ -+ /* Make a closing node, and hook it on the end. */ -+ ender = regnode(g, (paren) ? CLOSE+parno : END); -+ regtail(g, ret, ender); -+ -+ /* Hook the tails of the branches to the closing node. */ -+ for (br = ret; br != NULL; br = regnext(g, br)) -+ regoptail(g, br, ender); -+ -+ /* Check for proper termination. */ -+ if (paren && *g->regparse++ != ')') { -+ FAIL("unmatched ()"); -+ } else if (!paren && *g->regparse != '\0') { -+ if (*g->regparse == ')') { -+ FAIL("unmatched ()"); -+ } else -+ FAIL("junk on end"); /* "Can't happen". */ -+ /* NOTREACHED */ -+ } -+ -+ return(ret); -+} -+ -+/* -+ - regbranch - one alternative of an | operator -+ * -+ * Implements the concatenation operator. -+ */ -+static char * -+regbranch(struct match_globals *g, int *flagp) -+{ -+ register char *ret; -+ register char *chain; -+ register char *latest; -+ int flags; -+ -+ *flagp = WORST; /* Tentatively. */ -+ -+ ret = regnode(g, BRANCH); -+ chain = NULL; -+ while (*g->regparse != '\0' && *g->regparse != '|' && *g->regparse != ')') { -+ latest = regpiece(g, &flags); -+ if (latest == NULL) -+ return(NULL); -+ *flagp |= flags&HASWIDTH; -+ if (chain == NULL) /* First piece. */ -+ *flagp |= flags&SPSTART; -+ else -+ regtail(g, chain, latest); -+ chain = latest; -+ } -+ if (chain == NULL) /* Loop ran zero times. */ -+ (void) regnode(g, NOTHING); -+ -+ return(ret); -+} -+ -+/* -+ - regpiece - something followed by possible [*+?] -+ * -+ * Note that the branching code sequences used for ? and the general cases -+ * of * and + are somewhat optimized: they use the same NOTHING node as -+ * both the endmarker for their branch list and the body of the last branch. -+ * It might seem that this node could be dispensed with entirely, but the -+ * endmarker role is not redundant. -+ */ -+static char * -+regpiece(struct match_globals *g, int *flagp) -+{ -+ register char *ret; -+ register char op; -+ register char *next; -+ int flags; -+ -+ ret = regatom(g, &flags); -+ if (ret == NULL) -+ return(NULL); -+ -+ op = *g->regparse; -+ if (!ISMULT(op)) { -+ *flagp = flags; -+ return(ret); -+ } -+ -+ if (!(flags&HASWIDTH) && op != '?') -+ FAIL("*+ operand could be empty"); -+ *flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH); -+ -+ if (op == '*' && (flags&SIMPLE)) -+ reginsert(g, STAR, ret); -+ else if (op == '*') { -+ /* Emit x* as (x&|), where & means "self". */ -+ reginsert(g, BRANCH, ret); /* Either x */ -+ regoptail(g, ret, regnode(g, BACK)); /* and loop */ -+ regoptail(g, ret, ret); /* back */ -+ regtail(g, ret, regnode(g, BRANCH)); /* or */ -+ regtail(g, ret, regnode(g, NOTHING)); /* null. */ -+ } else if (op == '+' && (flags&SIMPLE)) -+ reginsert(g, PLUS, ret); -+ else if (op == '+') { -+ /* Emit x+ as x(&|), where & means "self". */ -+ next = regnode(g, BRANCH); /* Either */ -+ regtail(g, ret, next); -+ regtail(g, regnode(g, BACK), ret); /* loop back */ -+ regtail(g, next, regnode(g, BRANCH)); /* or */ -+ regtail(g, ret, regnode(g, NOTHING)); /* null. */ -+ } else if (op == '?') { -+ /* Emit x? as (x|) */ -+ reginsert(g, BRANCH, ret); /* Either x */ -+ regtail(g, ret, regnode(g, BRANCH)); /* or */ -+ next = regnode(g, NOTHING); /* null. */ -+ regtail(g, ret, next); -+ regoptail(g, ret, next); -+ } -+ g->regparse++; -+ if (ISMULT(*g->regparse)) -+ FAIL("nested *?+"); -+ -+ return(ret); -+} -+ -+/* -+ - regatom - the lowest level -+ * -+ * Optimization: gobbles an entire sequence of ordinary characters so that -+ * it can turn them into a single node, which is smaller to store and -+ * faster to run. Backslashed characters are exceptions, each becoming a -+ * separate node; the code is simpler that way and it's not worth fixing. -+ */ -+static char * -+regatom(struct match_globals *g, int *flagp) -+{ -+ register char *ret; -+ int flags; -+ -+ *flagp = WORST; /* Tentatively. */ -+ -+ switch (*g->regparse++) { -+ case '^': -+ ret = regnode(g, BOL); -+ break; -+ case '$': -+ ret = regnode(g, EOL); -+ break; -+ case '.': -+ ret = regnode(g, ANY); -+ *flagp |= HASWIDTH|SIMPLE; -+ break; -+ case '[': { -+ register int class; -+ register int classend; -+ -+ if (*g->regparse == '^') { /* Complement of range. */ -+ ret = regnode(g, ANYBUT); -+ g->regparse++; -+ } else -+ ret = regnode(g, ANYOF); -+ if (*g->regparse == ']' || *g->regparse == '-') -+ regc(g, *g->regparse++); -+ while (*g->regparse != '\0' && *g->regparse != ']') { -+ if (*g->regparse == '-') { -+ g->regparse++; -+ if (*g->regparse == ']' || *g->regparse == '\0') -+ regc(g, '-'); -+ else { -+ class = UCHARAT(g->regparse-2)+1; -+ classend = UCHARAT(g->regparse); -+ if (class > classend+1) -+ FAIL("invalid [] range"); -+ for (; class <= classend; class++) -+ regc(g, class); -+ g->regparse++; -+ } -+ } else -+ regc(g, *g->regparse++); -+ } -+ regc(g, '\0'); -+ if (*g->regparse != ']') -+ FAIL("unmatched []"); -+ g->regparse++; -+ *flagp |= HASWIDTH|SIMPLE; -+ } -+ break; -+ case '(': -+ ret = reg(g, 1, &flags); -+ if (ret == NULL) -+ return(NULL); -+ *flagp |= flags&(HASWIDTH|SPSTART); -+ break; -+ case '\0': -+ case '|': -+ case ')': -+ FAIL("internal urp"); /* Supposed to be caught earlier. */ -+ break; -+ case '?': -+ case '+': -+ case '*': -+ FAIL("?+* follows nothing"); -+ break; -+ case '\\': -+ if (*g->regparse == '\0') -+ FAIL("trailing \\"); -+ ret = regnode(g, EXACTLY); -+ regc(g, *g->regparse++); -+ regc(g, '\0'); -+ *flagp |= HASWIDTH|SIMPLE; -+ break; -+ default: { -+ register int len; -+ register char ender; -+ -+ g->regparse--; -+ len = my_strcspn((const char *)g->regparse, (const char *)META); -+ if (len <= 0) -+ FAIL("internal disaster"); -+ ender = *(g->regparse+len); -+ if (len > 1 && ISMULT(ender)) -+ len--; /* Back off clear of ?+* operand. */ -+ *flagp |= HASWIDTH; -+ if (len == 1) -+ *flagp |= SIMPLE; -+ ret = regnode(g, EXACTLY); -+ while (len > 0) { -+ regc(g, *g->regparse++); -+ len--; -+ } -+ regc(g, '\0'); -+ } -+ break; -+ } -+ -+ return(ret); -+} -+ -+/* -+ - regnode - emit a node -+ */ -+static char * /* Location. */ -+regnode(struct match_globals *g, char op) -+{ -+ register char *ret; -+ register char *ptr; -+ -+ ret = g->regcode; -+ if (ret == &g->regdummy) { -+ g->regsize += 3; -+ return(ret); -+ } -+ -+ ptr = ret; -+ *ptr++ = op; -+ *ptr++ = '\0'; /* Null "next" pointer. */ -+ *ptr++ = '\0'; -+ g->regcode = ptr; -+ -+ return(ret); -+} -+ -+/* -+ - regc - emit (if appropriate) a byte of code -+ */ -+static void -+regc(struct match_globals *g, char b) -+{ -+ if (g->regcode != &g->regdummy) -+ *g->regcode++ = b; -+ else -+ g->regsize++; -+} -+ -+/* -+ - reginsert - insert an operator in front of already-emitted operand -+ * -+ * Means relocating the operand. -+ */ -+static void -+reginsert(struct match_globals *g, char op, char* opnd) -+{ -+ register char *src; -+ register char *dst; -+ register char *place; -+ -+ if (g->regcode == &g->regdummy) { -+ g->regsize += 3; -+ return; -+ } -+ -+ src = g->regcode; -+ g->regcode += 3; -+ dst = g->regcode; -+ while (src > opnd) -+ *--dst = *--src; -+ -+ place = opnd; /* Op node, where operand used to be. */ -+ *place++ = op; -+ *place++ = '\0'; -+ *place++ = '\0'; -+} -+ -+/* -+ - regtail - set the next-pointer at the end of a node chain -+ */ -+static void -+regtail(struct match_globals *g, char *p, char *val) -+{ -+ register char *scan; -+ register char *temp; -+ register int offset; -+ -+ if (p == &g->regdummy) -+ return; -+ -+ /* Find last node. */ -+ scan = p; -+ for (;;) { -+ temp = regnext(g, scan); -+ if (temp == NULL) -+ break; -+ scan = temp; -+ } -+ -+ if (OP(scan) == BACK) -+ offset = scan - val; -+ else -+ offset = val - scan; -+ *(scan+1) = (offset>>8)&0377; -+ *(scan+2) = offset&0377; -+} -+ -+/* -+ - regoptail - regtail on operand of first argument; nop if operandless -+ */ -+static void -+regoptail(struct match_globals *g, char *p, char *val) -+{ -+ /* "Operandless" and "op != BRANCH" are synonymous in practice. */ -+ if (p == NULL || p == &g->regdummy || OP(p) != BRANCH) -+ return; -+ regtail(g, OPERAND(p), val); -+} -+ -+/* -+ * regexec and friends -+ */ -+ -+ -+/* -+ * Forwards. -+ */ -+STATIC int regtry(struct match_globals *g, regexp *prog, char *string); -+STATIC int regmatch(struct match_globals *g, char *prog); -+STATIC int regrepeat(struct match_globals *g, char *p); -+ -+#ifdef DEBUG -+int regnarrate = 0; -+void regdump(); -+STATIC char *regprop(char *op); -+#endif -+ -+/* -+ - regexec - match a regexp against a string -+ */ -+int -+regexec(regexp *prog, char *string) -+{ -+ register char *s; -+ struct match_globals g; -+ -+ /* Be paranoid... */ -+ if (prog == NULL || string == NULL) { -+ printk("<3>Regexp: NULL parameter\n"); -+ return(0); -+ } -+ -+ /* Check validity of program. */ -+ if (UCHARAT(prog->program) != MAGIC) { -+ printk("<3>Regexp: corrupted program\n"); -+ return(0); -+ } -+ -+ /* If there is a "must appear" string, look for it. */ -+ if (prog->regmust != NULL) { -+ s = string; -+ while ((s = strchr(s, prog->regmust[0])) != NULL) { -+ if (strncmp(s, prog->regmust, prog->regmlen) == 0) -+ break; /* Found it. */ -+ s++; -+ } -+ if (s == NULL) /* Not present. */ -+ return(0); -+ } -+ -+ /* Mark beginning of line for ^ . */ -+ g.regbol = string; -+ -+ /* Simplest case: anchored match need be tried only once. */ -+ if (prog->reganch) -+ return(regtry(&g, prog, string)); -+ -+ /* Messy cases: unanchored match. */ -+ s = string; -+ if (prog->regstart != '\0') -+ /* We know what char it must start with. */ -+ while ((s = strchr(s, prog->regstart)) != NULL) { -+ if (regtry(&g, prog, s)) -+ return(1); -+ s++; -+ } -+ else -+ /* We don't -- general case. */ -+ do { -+ if (regtry(&g, prog, s)) -+ return(1); -+ } while (*s++ != '\0'); -+ -+ /* Failure. */ -+ return(0); -+} -+ -+/* -+ - regtry - try match at specific point -+ */ -+static int /* 0 failure, 1 success */ -+regtry(struct match_globals *g, regexp *prog, char *string) -+{ -+ register int i; -+ register char **sp; -+ register char **ep; -+ -+ g->reginput = string; -+ g->regstartp = prog->startp; -+ g->regendp = prog->endp; -+ -+ sp = prog->startp; -+ ep = prog->endp; -+ for (i = NSUBEXP; i > 0; i--) { -+ *sp++ = NULL; -+ *ep++ = NULL; -+ } -+ if (regmatch(g, prog->program + 1)) { -+ prog->startp[0] = string; -+ prog->endp[0] = g->reginput; -+ return(1); -+ } else -+ return(0); -+} -+ -+/* -+ - regmatch - main matching routine -+ * -+ * Conceptually the strategy is simple: check to see whether the current -+ * node matches, call self recursively to see whether the rest matches, -+ * and then act accordingly. In practice we make some effort to avoid -+ * recursion, in particular by going through "ordinary" nodes (that don't -+ * need to know whether the rest of the match failed) by a loop instead of -+ * by recursion. -+ */ -+static int /* 0 failure, 1 success */ -+regmatch(struct match_globals *g, char *prog) -+{ -+ register char *scan = prog; /* Current node. */ -+ char *next; /* Next node. */ -+ -+#ifdef DEBUG -+ if (scan != NULL && regnarrate) -+ fprintf(stderr, "%s(\n", regprop(scan)); -+#endif -+ while (scan != NULL) { -+#ifdef DEBUG -+ if (regnarrate) -+ fprintf(stderr, "%s...\n", regprop(scan)); -+#endif -+ next = regnext(g, scan); -+ -+ switch (OP(scan)) { -+ case BOL: -+ if (g->reginput != g->regbol) -+ return(0); -+ break; -+ case EOL: -+ if (*g->reginput != '\0') -+ return(0); -+ break; -+ case ANY: -+ if (*g->reginput == '\0') -+ return(0); -+ g->reginput++; -+ break; -+ case EXACTLY: { -+ register int len; -+ register char *opnd; -+ -+ opnd = OPERAND(scan); -+ /* Inline the first character, for speed. */ -+ if (*opnd != *g->reginput) -+ return(0); -+ len = strlen(opnd); -+ if (len > 1 && strncmp(opnd, g->reginput, len) != 0) -+ return(0); -+ g->reginput += len; -+ } -+ break; -+ case ANYOF: -+ if (*g->reginput == '\0' || strchr(OPERAND(scan), *g->reginput) == NULL) -+ return(0); -+ g->reginput++; -+ break; -+ case ANYBUT: -+ if (*g->reginput == '\0' || strchr(OPERAND(scan), *g->reginput) != NULL) -+ return(0); -+ g->reginput++; -+ break; -+ case NOTHING: -+ case BACK: -+ break; -+ case OPEN+1: -+ case OPEN+2: -+ case OPEN+3: -+ case OPEN+4: -+ case OPEN+5: -+ case OPEN+6: -+ case OPEN+7: -+ case OPEN+8: -+ case OPEN+9: { -+ register int no; -+ register char *save; -+ -+ no = OP(scan) - OPEN; -+ save = g->reginput; -+ -+ if (regmatch(g, next)) { -+ /* -+ * Don't set startp if some later -+ * invocation of the same parentheses -+ * already has. -+ */ -+ if (g->regstartp[no] == NULL) -+ g->regstartp[no] = save; -+ return(1); -+ } else -+ return(0); -+ } -+ break; -+ case CLOSE+1: -+ case CLOSE+2: -+ case CLOSE+3: -+ case CLOSE+4: -+ case CLOSE+5: -+ case CLOSE+6: -+ case CLOSE+7: -+ case CLOSE+8: -+ case CLOSE+9: -+ { -+ register int no; -+ register char *save; -+ -+ no = OP(scan) - CLOSE; -+ save = g->reginput; -+ -+ if (regmatch(g, next)) { -+ /* -+ * Don't set endp if some later -+ * invocation of the same parentheses -+ * already has. -+ */ -+ if (g->regendp[no] == NULL) -+ g->regendp[no] = save; -+ return(1); -+ } else -+ return(0); -+ } -+ break; -+ case BRANCH: { -+ register char *save; -+ -+ if (OP(next) != BRANCH) /* No choice. */ -+ next = OPERAND(scan); /* Avoid recursion. */ -+ else { -+ do { -+ save = g->reginput; -+ if (regmatch(g, OPERAND(scan))) -+ return(1); -+ g->reginput = save; -+ scan = regnext(g, scan); -+ } while (scan != NULL && OP(scan) == BRANCH); -+ return(0); -+ /* NOTREACHED */ -+ } -+ } -+ break; -+ case STAR: -+ case PLUS: { -+ register char nextch; -+ register int no; -+ register char *save; -+ register int min; -+ -+ /* -+ * Lookahead to avoid useless match attempts -+ * when we know what character comes next. -+ */ -+ nextch = '\0'; -+ if (OP(next) == EXACTLY) -+ nextch = *OPERAND(next); -+ min = (OP(scan) == STAR) ? 0 : 1; -+ save = g->reginput; -+ no = regrepeat(g, OPERAND(scan)); -+ while (no >= min) { -+ /* If it could work, try it. */ -+ if (nextch == '\0' || *g->reginput == nextch) -+ if (regmatch(g, next)) -+ return(1); -+ /* Couldn't or didn't -- back up. */ -+ no--; -+ g->reginput = save + no; -+ } -+ return(0); -+ } -+ break; -+ case END: -+ return(1); /* Success! */ -+ break; -+ default: -+ printk("<3>Regexp: memory corruption\n"); -+ return(0); -+ break; -+ } -+ -+ scan = next; -+ } -+ -+ /* -+ * We get here only if there's trouble -- normally "case END" is -+ * the terminating point. -+ */ -+ printk("<3>Regexp: corrupted pointers\n"); -+ return(0); -+} -+ -+/* -+ - regrepeat - repeatedly match something simple, report how many -+ */ -+static int -+regrepeat(struct match_globals *g, char *p) -+{ -+ register int count = 0; -+ register char *scan; -+ register char *opnd; -+ -+ scan = g->reginput; -+ opnd = OPERAND(p); -+ switch (OP(p)) { -+ case ANY: -+ count = strlen(scan); -+ scan += count; -+ break; -+ case EXACTLY: -+ while (*opnd == *scan) { -+ count++; -+ scan++; -+ } -+ break; -+ case ANYOF: -+ while (*scan != '\0' && strchr(opnd, *scan) != NULL) { -+ count++; -+ scan++; -+ } -+ break; -+ case ANYBUT: -+ while (*scan != '\0' && strchr(opnd, *scan) == NULL) { -+ count++; -+ scan++; -+ } -+ break; -+ default: /* Oh dear. Called inappropriately. */ -+ printk("<3>Regexp: internal foulup\n"); -+ count = 0; /* Best compromise. */ -+ break; -+ } -+ g->reginput = scan; -+ -+ return(count); -+} -+ -+/* -+ - regnext - dig the "next" pointer out of a node -+ */ -+static char* -+regnext(struct match_globals *g, char *p) -+{ -+ register int offset; -+ -+ if (p == &g->regdummy) -+ return(NULL); -+ -+ offset = NEXT(p); -+ if (offset == 0) -+ return(NULL); -+ -+ if (OP(p) == BACK) -+ return(p-offset); -+ else -+ return(p+offset); -+} -+ -+#ifdef DEBUG -+ -+STATIC char *regprop(); -+ -+/* -+ - regdump - dump a regexp onto stdout in vaguely comprehensible form -+ */ -+void -+regdump(regexp *r) -+{ -+ register char *s; -+ register char op = EXACTLY; /* Arbitrary non-END op. */ -+ register char *next; -+ /* extern char *strchr(); */ -+ -+ -+ s = r->program + 1; -+ while (op != END) { /* While that wasn't END last time... */ -+ op = OP(s); -+ printf("%2d%s", s-r->program, regprop(s)); /* Where, what. */ -+ next = regnext(s); -+ if (next == NULL) /* Next ptr. */ -+ printf("(0)"); -+ else -+ printf("(%d)", (s-r->program)+(next-s)); -+ s += 3; -+ if (op == ANYOF || op == ANYBUT || op == EXACTLY) { -+ /* Literal string, where present. */ -+ while (*s != '\0') { -+ putchar(*s); -+ s++; -+ } -+ s++; -+ } -+ putchar('\n'); -+ } -+ -+ /* Header fields of interest. */ -+ if (r->regstart != '\0') -+ printf("start `%c' ", r->regstart); -+ if (r->reganch) -+ printf("anchored "); -+ if (r->regmust != NULL) -+ printf("must have \"%s\"", r->regmust); -+ printf("\n"); -+} -+ -+/* -+ - regprop - printable representation of opcode -+ */ -+static char * -+regprop(char *op) -+{ -+#define BUFLEN 50 -+ register char *p; -+ static char buf[BUFLEN]; -+ -+ strcpy(buf, ":"); -+ -+ switch (OP(op)) { -+ case BOL: -+ p = "BOL"; -+ break; -+ case EOL: -+ p = "EOL"; -+ break; -+ case ANY: -+ p = "ANY"; -+ break; -+ case ANYOF: -+ p = "ANYOF"; -+ break; -+ case ANYBUT: -+ p = "ANYBUT"; -+ break; -+ case BRANCH: -+ p = "BRANCH"; -+ break; -+ case EXACTLY: -+ p = "EXACTLY"; -+ break; -+ case NOTHING: -+ p = "NOTHING"; -+ break; -+ case BACK: -+ p = "BACK"; -+ break; -+ case END: -+ p = "END"; -+ break; -+ case OPEN+1: -+ case OPEN+2: -+ case OPEN+3: -+ case OPEN+4: -+ case OPEN+5: -+ case OPEN+6: -+ case OPEN+7: -+ case OPEN+8: -+ case OPEN+9: -+ snprintf(buf+strlen(buf),BUFLEN-strlen(buf), "OPEN%d", OP(op)-OPEN); -+ p = NULL; -+ break; -+ case CLOSE+1: -+ case CLOSE+2: -+ case CLOSE+3: -+ case CLOSE+4: -+ case CLOSE+5: -+ case CLOSE+6: -+ case CLOSE+7: -+ case CLOSE+8: -+ case CLOSE+9: -+ snprintf(buf+strlen(buf),BUFLEN-strlen(buf), "CLOSE%d", OP(op)-CLOSE); -+ p = NULL; -+ break; -+ case STAR: -+ p = "STAR"; -+ break; -+ case PLUS: -+ p = "PLUS"; -+ break; -+ default: -+ printk("<3>Regexp: corrupted opcode\n"); -+ break; -+ } -+ if (p != NULL) -+ strncat(buf, p, BUFLEN-strlen(buf)); -+ return(buf); -+} -+#endif -+ -+ ---- /dev/null -+++ b/net/ipv4/netfilter/regexp/regexp.h -@@ -0,0 +1,41 @@ -+/* -+ * Definitions etc. for regexp(3) routines. -+ * -+ * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof], -+ * not the System V one. -+ */ -+ -+#ifndef REGEXP_H -+#define REGEXP_H -+ -+ -+/* -+http://www.opensource.apple.com/darwinsource/10.3/expect-1/expect/expect.h , -+which contains a version of this library, says: -+ -+ * -+ * NSUBEXP must be at least 10, and no greater than 117 or the parser -+ * will not work properly. -+ * -+ -+However, it looks rather like this library is limited to 10. If you think -+otherwise, let us know. -+*/ -+ -+#define NSUBEXP 10 -+typedef struct regexp { -+ char *startp[NSUBEXP]; -+ char *endp[NSUBEXP]; -+ char regstart; /* Internal use only. */ -+ char reganch; /* Internal use only. */ -+ char *regmust; /* Internal use only. */ -+ int regmlen; /* Internal use only. */ -+ char program[1]; /* Unwarranted chumminess with compiler. */ -+} regexp; -+ -+regexp * regcomp(char *exp, int *patternsize); -+int regexec(regexp *prog, char *string); -+void regsub(regexp *prog, char *source, char *dest); -+void regerror(char *s); -+ -+#endif ---- /dev/null -+++ b/net/ipv4/netfilter/regexp/regmagic.h -@@ -0,0 +1,5 @@ -+/* -+ * The first byte of the regexp internal "program" is actually this magic -+ * number; the start node begins in the second byte. -+ */ -+#define MAGIC 0234 ---- /dev/null -+++ b/net/ipv4/netfilter/regexp/regsub.c -@@ -0,0 +1,95 @@ -+/* -+ * regsub -+ * @(#)regsub.c 1.3 of 2 April 86 -+ * -+ * Copyright (c) 1986 by University of Toronto. -+ * Written by Henry Spencer. Not derived from licensed software. -+ * -+ * Permission is granted to anyone to use this software for any -+ * purpose on any computer system, and to redistribute it freely, -+ * subject to the following restrictions: -+ * -+ * 1. The author is not responsible for the consequences of use of -+ * this software, no matter how awful, even if they arise -+ * from defects in it. -+ * -+ * 2. The origin of this software must not be misrepresented, either -+ * by explicit claim or by omission. -+ * -+ * 3. Altered versions must be plainly marked as such, and must not -+ * be misrepresented as being the original software. -+ * -+ * -+ * This code was modified by Ethan Sommer to work within the kernel -+ * (it now uses kmalloc etc..) -+ * -+ */ -+#include "regexp.h" -+#include "regmagic.h" -+#include -+ -+ -+#ifndef CHARBITS -+#define UCHARAT(p) ((int)*(unsigned char *)(p)) -+#else -+#define UCHARAT(p) ((int)*(p)&CHARBITS) -+#endif -+ -+#if 0 -+//void regerror(char * s) -+//{ -+// printk("regexp(3): %s", s); -+// /* NOTREACHED */ -+//} -+#endif -+ -+/* -+ - regsub - perform substitutions after a regexp match -+ */ -+void -+regsub(regexp * prog, char * source, char * dest) -+{ -+ register char *src; -+ register char *dst; -+ register char c; -+ register int no; -+ register int len; -+ -+ /* Not necessary and gcc doesn't like it -MLS */ -+ /*extern char *strncpy();*/ -+ -+ if (prog == NULL || source == NULL || dest == NULL) { -+ regerror("NULL parm to regsub"); -+ return; -+ } -+ if (UCHARAT(prog->program) != MAGIC) { -+ regerror("damaged regexp fed to regsub"); -+ return; -+ } -+ -+ src = source; -+ dst = dest; -+ while ((c = *src++) != '\0') { -+ if (c == '&') -+ no = 0; -+ else if (c == '\\' && '0' <= *src && *src <= '9') -+ no = *src++ - '0'; -+ else -+ no = -1; -+ -+ if (no < 0) { /* Ordinary character. */ -+ if (c == '\\' && (*src == '\\' || *src == '&')) -+ c = *src++; -+ *dst++ = c; -+ } else if (prog->startp[no] != NULL && prog->endp[no] != NULL) { -+ len = prog->endp[no] - prog->startp[no]; -+ (void) strncpy(dst, prog->startp[no], len); -+ dst += len; -+ if (len != 0 && *(dst-1) == '\0') { /* strncpy hit NUL. */ -+ regerror("damaged match string"); -+ return; -+ } -+ } -+ } -+ *dst++ = '\0'; -+} ---- /dev/null -+++ b/net/ipv4/netfilter/webmon_deps/tree_map.h -@@ -0,0 +1,1084 @@ -+/* -+ * Copyright © 2008 by Eric Bishop -+ * -+ * This work 'as-is' we provide. -+ * No warranty, express or implied. -+ * We've done our best, -+ * to debug and test. -+ * Liability for damages denied. -+ * -+ * Permission is granted hereby, -+ * to copy, share, and modify. -+ * Use as is fit, -+ * free or for profit. -+ * On this notice these rights rely. -+ * -+ * -+ * -+ * Note that unlike other portions of Gargoyle this code -+ * does not fall under the GPL, but the rather whimsical -+ * 'Poetic License' above. -+ * -+ * Basically, this library contains a bunch of utilities -+ * that I find useful. I'm sure other libraries exist -+ * that are just as good or better, but I like these tools -+ * because I personally wrote them, so I know their quirks. -+ * (i.e. I know where the bodies are buried). I want to -+ * make sure that I can re-use these utilities for whatever -+ * code I may want to write in the future be it -+ * proprietary or open-source, so I've put them under -+ * a very, very permissive license. -+ * -+ * If you find this code useful, use it. If not, don't. -+ * I really don't care. -+ * -+ */ -+ -+ -+#if __KERNEL__ -+ #define malloc(foo) kmalloc(foo,GFP_ATOMIC) -+ #define free(foo) kfree(foo) -+ #define printf(format,args...) printk(format,##args) -+ -+ /* kernel strdup */ -+ static inline char *kernel_strdup(const char *str); -+ static inline char *kernel_strdup(const char *str) -+ { -+ char *tmp; -+ long int s; -+ s=strlen(str) + 1; -+ tmp = kmalloc(s, GFP_ATOMIC); -+ if (tmp != NULL) -+ { -+ memcpy(tmp, str, s); -+ } -+ return tmp; -+ } -+ #define strdup kernel_strdup -+ -+#endif -+ -+ -+ -+/* tree_map structs / prototypes */ -+typedef struct long_tree_map_node -+{ -+ unsigned long key; -+ void* value; -+ -+ signed char balance; -+ struct long_tree_map_node* left; -+ struct long_tree_map_node* right; -+} long_map_node; -+ -+typedef struct -+{ -+ long_map_node* root; -+ unsigned long num_elements; -+ -+}long_map; -+ -+typedef struct -+{ -+ long_map lm; -+ unsigned char store_keys; -+ unsigned long num_elements; -+ -+}string_map; -+ -+ -+ -+/* long map functions */ -+long_map* initialize_long_map(void); -+void* get_long_map_element(long_map* map, unsigned long key); -+void* get_smallest_long_map_element(long_map* map, unsigned long* smallest_key); -+void* get_largest_long_map_element(long_map* map, unsigned long* largest_key); -+void* remove_smallest_long_map_element(long_map* map, unsigned long* smallest_key); -+void* remove_largest_long_map_element(long_map* map, unsigned long* largest_key); -+void* set_long_map_element(long_map* map, unsigned long key, void* value); -+void* remove_long_map_element(long_map* map, unsigned long key); -+unsigned long* get_sorted_long_map_keys(long_map* map, unsigned long* num_keys_returned); -+void** get_sorted_long_map_values(long_map* map, unsigned long* num_values_returned); -+void** destroy_long_map(long_map* map, int destruction_type, unsigned long* num_destroyed); -+void apply_to_every_long_map_value(long_map* map, void (*apply_func)(unsigned long key, void* value)); -+ -+/* string map functions */ -+string_map* initialize_string_map(unsigned char store_keys); -+void* get_string_map_element(string_map* map, const char* key); -+void* set_string_map_element(string_map* map, const char* key, void* value); -+void* remove_string_map_element(string_map* map, const char* key); -+char** get_string_map_keys(string_map* map, unsigned long* num_keys_returned); -+void** get_string_map_values(string_map* map, unsigned long* num_values_returned); -+void** destroy_string_map(string_map* map, int destruction_type, unsigned long* num_destroyed); -+void apply_to_every_string_map_value(string_map* map, void (*apply_func)(char* key, void* value)); -+ -+ -+/* -+ * three different ways to deal with values when data structure is destroyed -+ */ -+#define DESTROY_MODE_RETURN_VALUES 20 -+#define DESTROY_MODE_FREE_VALUES 21 -+#define DESTROY_MODE_IGNORE_VALUES 22 -+ -+ -+/* -+ * for convenience & backwards compatibility alias _string_map_ functions to -+ * _map_ functions since string map is used more often than long map -+ */ -+#define initialize_map initialize_string_map -+#define set_map_element set_string_map_element -+#define get_map_element get_string_map_element -+#define remove_map_element remove_string_map_element -+#define get_map_keys get_string_map_keys -+#define get_map_values get_string_map_values -+#define destroy_map destroy_string_map -+ -+ -+/* internal utility structures/ functions */ -+typedef struct stack_node_struct -+{ -+ long_map_node** node_ptr; -+ signed char direction; -+ struct stack_node_struct* previous; -+} stack_node; -+ -+static void free_stack(stack_node* stack); -+static void** destroy_long_map_values(long_map* map, int destruction_type, unsigned long* num_destroyed); -+static void apply_to_every_long_map_node(long_map_node* node, void (*apply_func)(unsigned long key, void* value)); -+static void apply_to_every_string_map_node(long_map_node* node, unsigned char has_key, void (*apply_func)(char* key, void* value)); -+static void get_sorted_node_keys(long_map_node* node, unsigned long* key_list, unsigned long* next_key_index, int depth); -+static void get_sorted_node_values(long_map_node* node, void** value_list, unsigned long* next_value_index, int depth); -+static signed char rebalance (long_map_node** n, signed char direction, signed char update_op); -+static void rotate_right (long_map_node** parent); -+static void rotate_left (long_map_node** parent); -+ -+/* internal for string map */ -+typedef struct -+{ -+ char* key; -+ void* value; -+} string_map_key_value; -+static unsigned long sdbm_string_hash(const char *key); -+ -+ -+ -+ -+/*************************************************** -+ * For testing only -+ ***************************************************/ -+/* -+void print_list(stack_node *l); -+ -+void print_list(stack_node *l) -+{ -+ if(l != NULL) -+ { -+ printf(" list key = %ld, dir=%d, \n", (*(l->node_ptr))->key, l->direction); -+ print_list(l->previous); -+ } -+} -+*/ -+/****************************************************** -+ * End testing Code -+ *******************************************************/ -+ -+ -+ -+ -+/*************************************************** -+ * string_map function definitions -+ ***************************************************/ -+ -+string_map* initialize_string_map(unsigned char store_keys) -+{ -+ string_map* map = (string_map*)malloc(sizeof(string_map)); -+ if(map != NULL) -+ { -+ map->store_keys = store_keys; -+ map->lm.root = NULL; -+ map->lm.num_elements = 0; -+ map->num_elements = map->lm.num_elements; -+ } -+ return map; -+} -+ -+void* get_string_map_element(string_map* map, const char* key) -+{ -+ unsigned long hashed_key = sdbm_string_hash(key); -+ void* return_value = get_long_map_element( &(map->lm), hashed_key); -+ if(return_value != NULL && map->store_keys) -+ { -+ string_map_key_value* r = (string_map_key_value*)return_value; -+ return_value = r->value; -+ } -+ map->num_elements = map->lm.num_elements; -+ return return_value; -+} -+ -+void* set_string_map_element(string_map* map, const char* key, void* value) -+{ -+ unsigned long hashed_key = sdbm_string_hash(key); -+ void* return_value = NULL; -+ if(map->store_keys) -+ { -+ string_map_key_value* kv = (string_map_key_value*)malloc(sizeof(string_map_key_value)); -+ if(kv == NULL) /* deal with malloc failure */ -+ { -+ return NULL; -+ } -+ kv->key = strdup(key); -+ if(kv->key == NULL) /* deal with malloc failure */ -+ { -+ free(kv); -+ return NULL; -+ } -+ kv->value = value; -+ return_value = set_long_map_element( &(map->lm), hashed_key, kv); -+ if(return_value != NULL) -+ { -+ string_map_key_value* r = (string_map_key_value*)return_value; -+ return_value = r->value; -+ free(r->key); -+ free(r); -+ } -+ } -+ else -+ { -+ return_value = set_long_map_element( &(map->lm), hashed_key, value); -+ } -+ map->num_elements = map->lm.num_elements; -+ return return_value; -+} -+ -+void* remove_string_map_element(string_map* map, const char* key) -+{ -+ unsigned long hashed_key = sdbm_string_hash(key); -+ void* return_value = remove_long_map_element( &(map->lm), hashed_key); -+ -+ if(return_value != NULL && map->store_keys) -+ { -+ string_map_key_value* r = (string_map_key_value*)return_value; -+ return_value = r->value; -+ free(r->key); -+ free(r); -+ } -+ map->num_elements = map->lm.num_elements; -+ return return_value; -+} -+ -+char** get_string_map_keys(string_map* map, unsigned long* num_keys_returned) -+{ -+ char** str_keys; -+ str_keys = (char**)malloc((map->num_elements+1)*sizeof(char*)); -+ if(str_keys == NULL) /* deal with malloc failure */ -+ { -+ return NULL; -+ } -+ str_keys[0] = NULL; -+ *num_keys_returned = 0; -+ if(map->store_keys && map->num_elements > 0) -+ { -+ unsigned long list_length; -+ void** long_values = get_sorted_long_map_values( &(map->lm), &list_length); -+ unsigned long key_index; -+ /*list_length will be 0 on malloc failure in get_sorted_long_map_values, so this code shouldn't seg fault if that happens */ -+ for(key_index = 0; key_index < list_length; key_index++) -+ { -+ str_keys[key_index] = strdup( ((string_map_key_value*)(long_values[key_index]))->key); -+ if(str_keys[key_index] == NULL) /* deal with malloc failure */ -+ { -+ //just return the incomplete list (hey, it's null terminated...) -+ free(long_values); -+ return str_keys; -+ } -+ *num_keys_returned = *num_keys_returned + 1; -+ } -+ str_keys[list_length] = NULL; -+ free(long_values); -+ } -+ return str_keys; -+} -+ -+ -+void** get_string_map_values(string_map* map, unsigned long* num_values_returned) -+{ -+ void** values = NULL; -+ if(map != NULL) -+ { -+ values = get_sorted_long_map_values ( &(map->lm), num_values_returned ); -+ } -+ return values; -+} -+ -+ -+void** destroy_string_map(string_map* map, int destruction_type, unsigned long* num_destroyed) -+{ -+ void** return_values = NULL; -+ if(map != NULL) -+ { -+ if(map->store_keys) -+ { -+ void** kvs = destroy_long_map_values( &(map->lm), DESTROY_MODE_RETURN_VALUES, num_destroyed ); -+ unsigned long kv_index = 0; -+ for(kv_index=0; kv_index < *num_destroyed; kv_index++) -+ { -+ string_map_key_value* kv = (string_map_key_value*)kvs[kv_index]; -+ void* value = kv->value; -+ -+ free(kv->key); -+ free(kv); -+ if(destruction_type == DESTROY_MODE_FREE_VALUES) -+ { -+ free(value); -+ } -+ if(destruction_type == DESTROY_MODE_RETURN_VALUES) -+ { -+ kvs[kv_index] = value; -+ } -+ } -+ if(destruction_type == DESTROY_MODE_RETURN_VALUES) -+ { -+ return_values = kvs; -+ } -+ else -+ { -+ free(kvs); -+ } -+ } -+ else -+ { -+ return_values = destroy_long_map_values( &(map->lm), destruction_type, num_destroyed ); -+ } -+ free(map); -+ } -+ return return_values; -+} -+ -+ -+ -+ -+/*************************************************** -+ * long_map function definitions -+ ***************************************************/ -+ -+long_map* initialize_long_map(void) -+{ -+ long_map* map = (long_map*)malloc(sizeof(long_map)); -+ if(map != NULL) /* test for malloc failure */ -+ { -+ map->root = NULL; -+ map->num_elements = 0; -+ } -+ return map; -+} -+ -+void* get_long_map_element(long_map* map, unsigned long key) -+{ -+ void* value = NULL; -+ -+ if(map->root != NULL) -+ { -+ long_map_node* parent_node = map->root; -+ long_map_node* next_node; -+ while( key != parent_node->key && (next_node = (long_map_node *)(key < parent_node->key ? parent_node->left : parent_node->right)) != NULL) -+ { -+ parent_node = next_node; -+ } -+ if(parent_node->key == key) -+ { -+ value = parent_node->value; -+ } -+ } -+ return value; -+} -+ -+void* get_smallest_long_map_element(long_map* map, unsigned long* smallest_key) -+{ -+ void* value = NULL; -+ if(map->root != NULL) -+ { -+ long_map_node* next_node = map->root; -+ while( next_node->left != NULL) -+ { -+ next_node = next_node->left; -+ } -+ value = next_node->value; -+ *smallest_key = next_node->key; -+ } -+ return value; -+} -+ -+void* get_largest_long_map_element(long_map* map, unsigned long* largest_key) -+{ -+ void* value = NULL; -+ if(map->root != NULL) -+ { -+ long_map_node* next_node = map->root; -+ while( next_node->right != NULL) -+ { -+ next_node = next_node->right; -+ } -+ value = next_node->value; -+ *largest_key = next_node->key; -+ } -+ return value; -+} -+ -+void* remove_smallest_long_map_element(long_map* map, unsigned long* smallest_key) -+{ -+ get_smallest_long_map_element(map, smallest_key); -+ return remove_long_map_element(map, *smallest_key); -+} -+ -+void* remove_largest_long_map_element(long_map* map, unsigned long* largest_key) -+{ -+ get_largest_long_map_element(map, largest_key); -+ return remove_long_map_element(map, *largest_key); -+} -+ -+ -+/* if replacement performed, returns replaced value, otherwise null */ -+void* set_long_map_element(long_map* map, unsigned long key, void* value) -+{ -+ stack_node* parent_list = NULL; -+ void* old_value = NULL; -+ int old_value_found = 0; -+ -+ long_map_node* parent_node; -+ long_map_node* next_node; -+ stack_node* next_parent; -+ stack_node* previous_parent; -+ signed char new_balance; -+ -+ -+ long_map_node* new_node = (long_map_node*)malloc(sizeof(long_map_node)); -+ if(new_node == NULL) -+ { -+ return NULL; -+ } -+ new_node->value = value; -+ new_node->key = key; -+ new_node->left = NULL; -+ new_node->right = NULL; -+ new_node->balance = 0; -+ -+ -+ -+ if(map->root == NULL) -+ { -+ map->root = new_node; -+ } -+ else -+ { -+ parent_node = map->root; -+ -+ next_parent = (stack_node*)malloc(sizeof(stack_node)); -+ if(next_parent == NULL) /* deal with malloc failure */ -+ { -+ free(new_node); -+ return NULL; /* won't insert but won't seg fault */ -+ } -+ next_parent->node_ptr = &(map->root); -+ next_parent->previous = parent_list; -+ parent_list = next_parent; -+ -+ while( key != parent_node->key && (next_node = (key < parent_node->key ? parent_node->left : parent_node->right) ) != NULL) -+ { -+ next_parent = (stack_node*)malloc(sizeof(stack_node)); -+ if(next_parent == NULL) /* deal with malloc failure */ -+ { -+ /* free previous stack nodes to prevent memory leak */ -+ free_stack(parent_list); -+ free(new_node); -+ return NULL; -+ } -+ next_parent->node_ptr = key < parent_node->key ? &(parent_node->left) : &(parent_node->right); -+ next_parent->previous = parent_list; -+ next_parent->previous->direction = key < parent_node->key ? -1 : 1; -+ parent_list = next_parent; -+ -+ parent_node = next_node; -+ } -+ -+ -+ if(key == parent_node->key) -+ { -+ old_value = parent_node->value; -+ old_value_found = 1; -+ parent_node->value = value; -+ free(new_node); -+ /* we merely replaced a node, no need to rebalance */ -+ } -+ else -+ { -+ if(key < parent_node->key) -+ { -+ parent_node->left = (void*)new_node; -+ parent_list->direction = -1; -+ } -+ else -+ { -+ parent_node->right = (void*)new_node; -+ parent_list->direction = 1; -+ } -+ -+ -+ /* we inserted a node, rebalance */ -+ previous_parent = parent_list; -+ new_balance = 1; /* initial value is not used, but must not be 0 for initial loop condition */ -+ -+ -+ while(previous_parent != NULL && new_balance != 0) -+ { -+ new_balance = rebalance(previous_parent->node_ptr, previous_parent->direction, 1); -+ previous_parent = previous_parent->previous; -+ } -+ } -+ } -+ -+ free_stack(parent_list); -+ -+ if(old_value_found == 0) -+ { -+ map->num_elements = map->num_elements + 1; -+ } -+ -+ return old_value; -+} -+ -+ -+void* remove_long_map_element(long_map* map, unsigned long key) -+{ -+ -+ void* value = NULL; -+ -+ long_map_node* root_node = map->root; -+ stack_node* parent_list = NULL; -+ -+ -+ long_map_node* remove_parent; -+ long_map_node* remove_node; -+ long_map_node* next_node; -+ -+ long_map_node* replacement; -+ long_map_node* replacement_parent; -+ long_map_node* replacement_next; -+ -+ stack_node* next_parent; -+ stack_node* previous_parent; -+ stack_node* replacement_stack_node; -+ -+ -+ signed char new_balance; -+ -+ -+ -+ if(root_node != NULL) -+ { -+ remove_parent = root_node; -+ remove_node = key < remove_parent->key ? remove_parent->left : remove_parent->right; -+ -+ if(remove_node != NULL && key != remove_parent->key) -+ { -+ next_parent = (stack_node*)malloc(sizeof(stack_node)); -+ if(next_parent == NULL) /* deal with malloc failure */ -+ { -+ return NULL; -+ } -+ next_parent->node_ptr = &(map->root); -+ next_parent->previous = parent_list; -+ parent_list = next_parent; -+ while( key != remove_node->key && (next_node = (key < remove_node->key ? remove_node->left : remove_node->right)) != NULL) -+ { -+ next_parent = (stack_node*)malloc(sizeof(stack_node)); -+ if(next_parent == NULL) /* deal with malloc failure */ -+ { -+ /* free previous stack nodes to prevent memory leak */ -+ free_stack(parent_list); -+ return NULL; -+ } -+ next_parent->node_ptr = key < remove_parent->key ? &(remove_parent->left) : &(remove_parent->right); -+ next_parent->previous = parent_list; -+ next_parent->previous->direction = key < remove_parent->key ? -1 : 1; -+ parent_list = next_parent; -+ -+ -+ remove_parent = remove_node; -+ remove_node = next_node; -+ } -+ parent_list->direction = key < remove_parent-> key ? -1 : 1; -+ } -+ else -+ { -+ remove_node = remove_parent; -+ } -+ -+ -+ if(key == remove_node->key) -+ { -+ -+ /* find replacement for node we are deleting */ -+ if( remove_node->right == NULL ) -+ { -+ replacement = remove_node->left; -+ } -+ else if( remove_node->right->left == NULL) -+ { -+ -+ replacement = remove_node->right; -+ replacement->left = remove_node->left; -+ replacement->balance = remove_node->balance; -+ -+ /* put pointer to replacement node into list for balance update */ -+ replacement_stack_node = (stack_node*)malloc(sizeof(stack_node)); -+ if(replacement_stack_node == NULL) /* deal with malloc failure */ -+ { -+ /* free previous stack nodes to prevent memory leak */ -+ free_stack(parent_list); -+ return NULL; -+ } -+ replacement_stack_node->previous = parent_list; -+ replacement_stack_node->direction = 1; /* replacement is from right */ -+ if(remove_node == remove_parent) /* special case for root node */ -+ { -+ replacement_stack_node->node_ptr = &(map->root); -+ } -+ else -+ { -+ replacement_stack_node->node_ptr = key < remove_parent-> key ? &(remove_parent->left) : &(remove_parent->right); -+ } -+ parent_list = replacement_stack_node; -+ -+ } -+ else -+ { -+ /* put pointer to replacement node into list for balance update */ -+ replacement_stack_node = (stack_node*)malloc(sizeof(stack_node)); -+ if(replacement_stack_node == NULL) /* deal with malloc failure */ -+ { -+ /* free previous stack nodes to prevent memory leak */ -+ free_stack(parent_list); -+ return NULL; -+ } -+ -+ replacement_stack_node->previous = parent_list; -+ replacement_stack_node->direction = 1; /* we always look for replacement on right */ -+ if(remove_node == remove_parent) /* special case for root node */ -+ { -+ replacement_stack_node->node_ptr = &(map->root); -+ } -+ else -+ { -+ replacement_stack_node->node_ptr = key < remove_parent-> key ? &(remove_parent->left) : &(remove_parent->right); -+ } -+ -+ parent_list = replacement_stack_node; -+ -+ -+ /* -+ * put pointer to replacement node->right into list for balance update -+ * this node will have to be updated with the proper pointer -+ * after we have identified the replacement -+ */ -+ replacement_stack_node = (stack_node*)malloc(sizeof(stack_node)); -+ if(replacement_stack_node == NULL) /* deal with malloc failure */ -+ { -+ /* free previous stack nodes to prevent memory leak */ -+ free_stack(parent_list); -+ return NULL; -+ } -+ -+ replacement_stack_node->previous = parent_list; -+ replacement_stack_node->direction = -1; /* we always look for replacement to left of this node */ -+ parent_list = replacement_stack_node; -+ -+ /* find smallest node on right (large) side of tree */ -+ replacement_parent = remove_node->right; -+ replacement = replacement_parent->left; -+ -+ while((replacement_next = replacement->left) != NULL) -+ { -+ next_parent = (stack_node*)malloc(sizeof(stack_node)); -+ if(next_parent == NULL) /* deal with malloc failure */ -+ { -+ /* free previous stack nodes to prevent memory leak */ -+ free_stack(parent_list); -+ return NULL; -+ } -+ -+ next_parent->node_ptr = &(replacement_parent->left); -+ next_parent->previous = parent_list; -+ next_parent->direction = -1; /* we always go left */ -+ parent_list = next_parent; -+ -+ replacement_parent = replacement; -+ replacement = replacement_next; -+ -+ } -+ -+ replacement_parent->left = replacement->right; -+ -+ replacement->left = remove_node->left; -+ replacement->right = remove_node->right; -+ replacement->balance = remove_node->balance; -+ replacement_stack_node->node_ptr = &(replacement->right); -+ } -+ -+ /* insert replacement at proper location in tree */ -+ if(remove_node == remove_parent) -+ { -+ map->root = replacement; -+ } -+ else -+ { -+ remove_parent->left = remove_node == remove_parent->left ? replacement : remove_parent->left; -+ remove_parent->right = remove_node == remove_parent->right ? replacement : remove_parent->right; -+ } -+ -+ -+ /* rebalance tree */ -+ previous_parent = parent_list; -+ new_balance = 0; -+ while(previous_parent != NULL && new_balance == 0) -+ { -+ new_balance = rebalance(previous_parent->node_ptr, previous_parent->direction, -1); -+ previous_parent = previous_parent->previous; -+ } -+ -+ -+ -+ -+ /* -+ * since we found a value to remove, decrease number of elements in map -+ * set return value to the deleted node's value and free the node -+ */ -+ map->num_elements = map->num_elements - 1; -+ value = remove_node->value; -+ free(remove_node); -+ } -+ } -+ -+ free_stack(parent_list); -+ -+ return value; -+} -+ -+ -+/* note: returned keys are dynamically allocated, you need to free them! */ -+unsigned long* get_sorted_long_map_keys(long_map* map, unsigned long* num_keys_returned) -+{ -+ unsigned long* key_list = (unsigned long*)malloc((map->num_elements)*sizeof(unsigned long)); -+ unsigned long next_key_index; -+ if(key_list == NULL) -+ { -+ *num_keys_returned = 0; -+ return NULL; -+ } -+ next_key_index = 0; -+ get_sorted_node_keys(map->root, key_list, &next_key_index, 0); -+ -+ *num_keys_returned = map->num_elements; -+ -+ return key_list; -+} -+ -+ -+void** get_sorted_long_map_values(long_map* map, unsigned long* num_values_returned) -+{ -+ void** value_list = (void**)malloc((map->num_elements+1)*sizeof(void*)); -+ unsigned long next_value_index; -+ -+ if(value_list == NULL) -+ { -+ *num_values_returned = 0; -+ return NULL; -+ } -+ next_value_index = 0; -+ get_sorted_node_values(map->root, value_list, &next_value_index, 0); -+ value_list[map->num_elements] = NULL; /* since we're dealing with pointers make list null terminated */ -+ -+ *num_values_returned = map->num_elements; -+ return value_list; -+ -+} -+ -+ -+ -+void** destroy_long_map(long_map* map, int destruction_type, unsigned long* num_destroyed) -+{ -+ void** return_values = destroy_long_map_values(map, destruction_type, num_destroyed); -+ free(map); -+ return return_values; -+} -+ -+ -+ -+void apply_to_every_long_map_value(long_map* map, void (*apply_func)(unsigned long key, void* value)) -+{ -+ apply_to_every_long_map_node(map->root, apply_func); -+} -+void apply_to_every_string_map_value(string_map* map, void (*apply_func)(char* key, void* value)) -+{ -+ apply_to_every_string_map_node( (map->lm).root, map->store_keys, apply_func); -+} -+ -+ -+/*************************************************** -+ * internal utility function definitions -+ ***************************************************/ -+static void free_stack(stack_node* stack) -+{ -+ while(stack != NULL) -+ { -+ stack_node* prev_node = stack; -+ stack = prev_node->previous; -+ free(prev_node); -+ } -+ -+} -+ -+static void** destroy_long_map_values(long_map* map, int destruction_type, unsigned long* num_destroyed) -+{ -+ void** return_values = NULL; -+ unsigned long return_index = 0; -+ -+ *num_destroyed = 0; -+ -+ if(destruction_type == DESTROY_MODE_RETURN_VALUES) -+ { -+ return_values = (void**)malloc((map->num_elements+1)*sizeof(void*)); -+ if(return_values == NULL) /* deal with malloc failure */ -+ { -+ destruction_type = DESTROY_MODE_IGNORE_VALUES; /* could cause memory leak, but there's no other way to be sure we won't seg fault */ -+ } -+ else -+ { -+ return_values[map->num_elements] = NULL; -+ } -+ } -+ while(map->num_elements > 0) -+ { -+ unsigned long smallest_key; -+ void* removed_value = remove_smallest_long_map_element(map, &smallest_key); -+ if(destruction_type == DESTROY_MODE_RETURN_VALUES) -+ { -+ return_values[return_index] = removed_value; -+ } -+ if(destruction_type == DESTROY_MODE_FREE_VALUES) -+ { -+ free(removed_value); -+ } -+ return_index++; -+ *num_destroyed = *num_destroyed + 1; -+ } -+ return return_values; -+} -+ -+static void apply_to_every_long_map_node(long_map_node* node, void (*apply_func)(unsigned long key, void* value)) -+{ -+ if(node != NULL) -+ { -+ apply_to_every_long_map_node(node->left, apply_func); -+ -+ apply_func(node->key, node->value); -+ -+ apply_to_every_long_map_node(node->right, apply_func); -+ } -+} -+static void apply_to_every_string_map_node(long_map_node* node, unsigned char has_key, void (*apply_func)(char* key, void* value)) -+{ -+ if(node != NULL) -+ { -+ apply_to_every_string_map_node(node->left, has_key, apply_func); -+ -+ if(has_key) -+ { -+ string_map_key_value* kv = (string_map_key_value*)(node->value); -+ apply_func(kv->key, kv->value); -+ } -+ else -+ { -+ apply_func(NULL, node->value); -+ } -+ apply_to_every_string_map_node(node->right, has_key, apply_func); -+ } -+} -+ -+ -+ -+static void get_sorted_node_keys(long_map_node* node, unsigned long* key_list, unsigned long* next_key_index, int depth) -+{ -+ if(node != NULL) -+ { -+ get_sorted_node_keys(node->left, key_list, next_key_index, depth+1); -+ -+ key_list[ *next_key_index ] = node->key; -+ (*next_key_index)++; -+ -+ get_sorted_node_keys(node->right, key_list, next_key_index, depth+1); -+ } -+} -+ -+static void get_sorted_node_values(long_map_node* node, void** value_list, unsigned long* next_value_index, int depth) -+{ -+ if(node != NULL) -+ { -+ get_sorted_node_values(node->left, value_list, next_value_index, depth+1); -+ -+ value_list[ *next_value_index ] = node->value; -+ (*next_value_index)++; -+ -+ get_sorted_node_values(node->right, value_list, next_value_index, depth+1); -+ } -+} -+ -+ -+ -+/* -+ * direction = -1 indicates left subtree updated, direction = 1 for right subtree -+ * update_op = -1 indicates delete node, update_op = 1 for insert node -+ */ -+static signed char rebalance (long_map_node** n, signed char direction, signed char update_op) -+{ -+ /* -+ printf( "original: key = %ld, balance = %d, update_op=%d, direction=%d\n", (*n)->key, (*n)->balance, update_op, direction); -+ */ -+ -+ (*n)->balance = (*n)->balance + (update_op*direction); -+ -+ if( (*n)->balance < -1) -+ { -+ if((*n)->left->balance < 0) -+ { -+ rotate_right(n); -+ (*n)->right->balance = 0; -+ (*n)->balance = 0; -+ } -+ else if((*n)->left->balance == 0) -+ { -+ rotate_right(n); -+ (*n)->right->balance = -1; -+ (*n)->balance = 1; -+ } -+ else if((*n)->left->balance > 0) -+ { -+ rotate_left( &((*n)->left) ); -+ rotate_right(n); -+ /* -+ if( (*n)->balance < 0 ) -+ { -+ (*n)->left->balance = 0; -+ (*n)->right->balance = 1; -+ } -+ else if( (*n)->balance == 0 ) -+ { -+ (*n)->left->balance = 0; -+ (*n)->right->balance = 0; -+ } -+ else if( (*n)->balance > 0 ) -+ { -+ (*n)->left->balance = -1; -+ (*n)->right->balance = 0; -+ } -+ */ -+ (*n)->left->balance = (*n)->balance > 0 ? -1 : 0; -+ (*n)->right->balance = (*n)->balance < 0 ? 1 : 0; -+ (*n)->balance = 0; -+ } -+ } -+ if( (*n)->balance > 1) -+ { -+ if((*n)->right->balance > 0) -+ { -+ rotate_left(n); -+ (*n)->left->balance = 0; -+ (*n)->balance = 0; -+ } -+ else if ((*n)->right->balance == 0) -+ { -+ rotate_left(n); -+ (*n)->left->balance = 1; -+ (*n)->balance = -1; -+ } -+ else if((*n)->right->balance < 0) -+ { -+ rotate_right( &((*n)->right) ); -+ rotate_left(n); -+ /* -+ if( (*n)->balance < 0 ) -+ { -+ (*n)->left->balance = 0; -+ (*n)->right->balance = 1; -+ } -+ else if( (*n)->balance == 0 ) -+ { -+ (*n)->left->balance = 0; -+ (*n)->right->balance = 0; -+ } -+ else if( (*n)->balance > 0 ) -+ { -+ (*n)->left->balance = -1; -+ (*n)->right->balance = 0; -+ } -+ */ -+ (*n)->left->balance = (*n)->balance > 0 ? -1 : 0; -+ (*n)->right->balance = (*n)->balance < 0 ? 1 : 0; -+ (*n)->balance = 0; -+ } -+ } -+ -+ /* -+ printf( "key = %ld, balance = %d\n", (*n)->key, (*n)->balance); -+ */ -+ -+ return (*n)->balance; -+} -+ -+ -+static void rotate_right (long_map_node** parent) -+{ -+ long_map_node* old_parent = *parent; -+ long_map_node* pivot = old_parent->left; -+ old_parent->left = pivot->right; -+ pivot->right = old_parent; -+ -+ *parent = pivot; -+} -+ -+static void rotate_left (long_map_node** parent) -+{ -+ long_map_node* old_parent = *parent; -+ long_map_node* pivot = old_parent->right; -+ old_parent->right = pivot->left; -+ pivot->left = old_parent; -+ -+ *parent = pivot; -+} -+ -+ -+ -+/*************************************************************************** -+ * This algorithm was created for the sdbm database library (a public-domain -+ * reimplementation of ndbm) and seems to work relatively well in -+ * scrambling bits -+ * -+ * -+ * This code was derived from code found at: -+ * http://www.cse.yorku.ca/~oz/hash.html -+ ***************************************************************************/ -+static unsigned long sdbm_string_hash(const char *key) -+{ -+ unsigned long hashed_key = 0; -+ -+ int index = 0; -+ unsigned int nextch; -+ while(key[index] != '\0') -+ { -+ nextch = key[index]; -+ hashed_key = nextch + (hashed_key << 6) + (hashed_key << 16) - hashed_key; -+ index++; -+ } -+ return hashed_key; -+} -+ -+ ---- /dev/null -+++ b/net/ipv4/netfilter/weburl_deps/regexp.c -@@ -0,0 +1,1197 @@ -+/* -+ * regcomp and regexec -- regsub and regerror are elsewhere -+ * @(#)regexp.c 1.3 of 18 April 87 -+ * -+ * Copyright (c) 1986 by University of Toronto. -+ * Written by Henry Spencer. Not derived from licensed software. -+ * -+ * Permission is granted to anyone to use this software for any -+ * purpose on any computer system, and to redistribute it freely, -+ * subject to the following restrictions: -+ * -+ * 1. The author is not responsible for the consequences of use of -+ * this software, no matter how awful, even if they arise -+ * from defects in it. -+ * -+ * 2. The origin of this software must not be misrepresented, either -+ * by explicit claim or by omission. -+ * -+ * 3. Altered versions must be plainly marked as such, and must not -+ * be misrepresented as being the original software. -+ * -+ * Beware that some of this code is subtly aware of the way operator -+ * precedence is structured in regular expressions. Serious changes in -+ * regular-expression syntax might require a total rethink. -+ * -+ * This code was modified by Ethan Sommer to work within the kernel -+ * (it now uses kmalloc etc..) -+ * -+ * Modified slightly by Matthew Strait to use more modern C. -+ */ -+ -+#include "regexp.h" -+#include "regmagic.h" -+ -+/* added by ethan and matt. Lets it work in both kernel and user space. -+(So iptables can use it, for instance.) Yea, it goes both ways... */ -+#if __KERNEL__ -+ #define malloc(foo) kmalloc(foo,GFP_ATOMIC) -+#else -+ #define printk(format,args...) printf(format,##args) -+#endif -+ -+void regerror(char * s) -+{ -+ printk("<3>Regexp: %s\n", s); -+ /* NOTREACHED */ -+} -+ -+/* -+ * The "internal use only" fields in regexp.h are present to pass info from -+ * compile to execute that permits the execute phase to run lots faster on -+ * simple cases. They are: -+ * -+ * regstart char that must begin a match; '\0' if none obvious -+ * reganch is the match anchored (at beginning-of-line only)? -+ * regmust string (pointer into program) that match must include, or NULL -+ * regmlen length of regmust string -+ * -+ * Regstart and reganch permit very fast decisions on suitable starting points -+ * for a match, cutting down the work a lot. Regmust permits fast rejection -+ * of lines that cannot possibly match. The regmust tests are costly enough -+ * that regcomp() supplies a regmust only if the r.e. contains something -+ * potentially expensive (at present, the only such thing detected is * or + -+ * at the start of the r.e., which can involve a lot of backup). Regmlen is -+ * supplied because the test in regexec() needs it and regcomp() is computing -+ * it anyway. -+ */ -+ -+/* -+ * Structure for regexp "program". This is essentially a linear encoding -+ * of a nondeterministic finite-state machine (aka syntax charts or -+ * "railroad normal form" in parsing technology). Each node is an opcode -+ * plus a "next" pointer, possibly plus an operand. "Next" pointers of -+ * all nodes except BRANCH implement concatenation; a "next" pointer with -+ * a BRANCH on both ends of it is connecting two alternatives. (Here we -+ * have one of the subtle syntax dependencies: an individual BRANCH (as -+ * opposed to a collection of them) is never concatenated with anything -+ * because of operator precedence.) The operand of some types of node is -+ * a literal string; for others, it is a node leading into a sub-FSM. In -+ * particular, the operand of a BRANCH node is the first node of the branch. -+ * (NB this is *not* a tree structure: the tail of the branch connects -+ * to the thing following the set of BRANCHes.) The opcodes are: -+ */ -+ -+/* definition number opnd? meaning */ -+#define END 0 /* no End of program. */ -+#define BOL 1 /* no Match "" at beginning of line. */ -+#define EOL 2 /* no Match "" at end of line. */ -+#define ANY 3 /* no Match any one character. */ -+#define ANYOF 4 /* str Match any character in this string. */ -+#define ANYBUT 5 /* str Match any character not in this string. */ -+#define BRANCH 6 /* node Match this alternative, or the next... */ -+#define BACK 7 /* no Match "", "next" ptr points backward. */ -+#define EXACTLY 8 /* str Match this string. */ -+#define NOTHING 9 /* no Match empty string. */ -+#define STAR 10 /* node Match this (simple) thing 0 or more times. */ -+#define PLUS 11 /* node Match this (simple) thing 1 or more times. */ -+#define OPEN 20 /* no Mark this point in input as start of #n. */ -+ /* OPEN+1 is number 1, etc. */ -+#define CLOSE 30 /* no Analogous to OPEN. */ -+ -+/* -+ * Opcode notes: -+ * -+ * BRANCH The set of branches constituting a single choice are hooked -+ * together with their "next" pointers, since precedence prevents -+ * anything being concatenated to any individual branch. The -+ * "next" pointer of the last BRANCH in a choice points to the -+ * thing following the whole choice. This is also where the -+ * final "next" pointer of each individual branch points; each -+ * branch starts with the operand node of a BRANCH node. -+ * -+ * BACK Normal "next" pointers all implicitly point forward; BACK -+ * exists to make loop structures possible. -+ * -+ * STAR,PLUS '?', and complex '*' and '+', are implemented as circular -+ * BRANCH structures using BACK. Simple cases (one character -+ * per match) are implemented with STAR and PLUS for speed -+ * and to minimize recursive plunges. -+ * -+ * OPEN,CLOSE ...are numbered at compile time. -+ */ -+ -+/* -+ * A node is one char of opcode followed by two chars of "next" pointer. -+ * "Next" pointers are stored as two 8-bit pieces, high order first. The -+ * value is a positive offset from the opcode of the node containing it. -+ * An operand, if any, simply follows the node. (Note that much of the -+ * code generation knows about this implicit relationship.) -+ * -+ * Using two bytes for the "next" pointer is vast overkill for most things, -+ * but allows patterns to get big without disasters. -+ */ -+#define OP(p) (*(p)) -+#define NEXT(p) (((*((p)+1)&0377)<<8) + (*((p)+2)&0377)) -+#define OPERAND(p) ((p) + 3) -+ -+/* -+ * See regmagic.h for one further detail of program structure. -+ */ -+ -+ -+/* -+ * Utility definitions. -+ */ -+#ifndef CHARBITS -+#define UCHARAT(p) ((int)*(unsigned char *)(p)) -+#else -+#define UCHARAT(p) ((int)*(p)&CHARBITS) -+#endif -+ -+#define FAIL(m) { regerror(m); return(NULL); } -+#define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?') -+#define META "^$.[()|?+*\\" -+ -+/* -+ * Flags to be passed up and down. -+ */ -+#define HASWIDTH 01 /* Known never to match null string. */ -+#define SIMPLE 02 /* Simple enough to be STAR/PLUS operand. */ -+#define SPSTART 04 /* Starts with * or +. */ -+#define WORST 0 /* Worst case. */ -+ -+/* -+ * Global work variables for regcomp(). -+ */ -+struct match_globals { -+char *reginput; /* String-input pointer. */ -+char *regbol; /* Beginning of input, for ^ check. */ -+char **regstartp; /* Pointer to startp array. */ -+char **regendp; /* Ditto for endp. */ -+char *regparse; /* Input-scan pointer. */ -+int regnpar; /* () count. */ -+char regdummy; -+char *regcode; /* Code-emit pointer; ®dummy = don't. */ -+long regsize; /* Code size. */ -+}; -+ -+/* -+ * Forward declarations for regcomp()'s friends. -+ */ -+#ifndef STATIC -+#define STATIC static -+#endif -+STATIC char *reg(struct match_globals *g, int paren,int *flagp); -+STATIC char *regbranch(struct match_globals *g, int *flagp); -+STATIC char *regpiece(struct match_globals *g, int *flagp); -+STATIC char *regatom(struct match_globals *g, int *flagp); -+STATIC char *regnode(struct match_globals *g, char op); -+STATIC char *regnext(struct match_globals *g, char *p); -+STATIC void regc(struct match_globals *g, char b); -+STATIC void reginsert(struct match_globals *g, char op, char *opnd); -+STATIC void regtail(struct match_globals *g, char *p, char *val); -+STATIC void regoptail(struct match_globals *g, char *p, char *val); -+ -+ -+__kernel_size_t my_strcspn(const char *s1,const char *s2) -+{ -+ char *scan1; -+ char *scan2; -+ int count; -+ -+ count = 0; -+ for (scan1 = (char *)s1; *scan1 != '\0'; scan1++) { -+ for (scan2 = (char *)s2; *scan2 != '\0';) /* ++ moved down. */ -+ if (*scan1 == *scan2++) -+ return(count); -+ count++; -+ } -+ return(count); -+} -+ -+/* -+ - regcomp - compile a regular expression into internal code -+ * -+ * We can't allocate space until we know how big the compiled form will be, -+ * but we can't compile it (and thus know how big it is) until we've got a -+ * place to put the code. So we cheat: we compile it twice, once with code -+ * generation turned off and size counting turned on, and once "for real". -+ * This also means that we don't allocate space until we are sure that the -+ * thing really will compile successfully, and we never have to move the -+ * code and thus invalidate pointers into it. (Note that it has to be in -+ * one piece because free() must be able to free it all.) -+ * -+ * Beware that the optimization-preparation code in here knows about some -+ * of the structure of the compiled regexp. -+ */ -+regexp * -+regcomp(char *exp,int *patternsize) -+{ -+ register regexp *r; -+ register char *scan; -+ register char *longest; -+ register int len; -+ int flags; -+ struct match_globals g; -+ -+ /* commented out by ethan -+ extern char *malloc(); -+ */ -+ -+ if (exp == NULL) -+ FAIL("NULL argument"); -+ -+ /* First pass: determine size, legality. */ -+ g.regparse = exp; -+ g.regnpar = 1; -+ g.regsize = 0L; -+ g.regcode = &g.regdummy; -+ regc(&g, MAGIC); -+ if (reg(&g, 0, &flags) == NULL) -+ return(NULL); -+ -+ /* Small enough for pointer-storage convention? */ -+ if (g.regsize >= 32767L) /* Probably could be 65535L. */ -+ FAIL("regexp too big"); -+ -+ /* Allocate space. */ -+ *patternsize=sizeof(regexp) + (unsigned)g.regsize; -+ r = (regexp *)malloc(sizeof(regexp) + (unsigned)g.regsize); -+ if (r == NULL) -+ FAIL("out of space"); -+ -+ /* Second pass: emit code. */ -+ g.regparse = exp; -+ g.regnpar = 1; -+ g.regcode = r->program; -+ regc(&g, MAGIC); -+ if (reg(&g, 0, &flags) == NULL) -+ return(NULL); -+ -+ /* Dig out information for optimizations. */ -+ r->regstart = '\0'; /* Worst-case defaults. */ -+ r->reganch = 0; -+ r->regmust = NULL; -+ r->regmlen = 0; -+ scan = r->program+1; /* First BRANCH. */ -+ if (OP(regnext(&g, scan)) == END) { /* Only one top-level choice. */ -+ scan = OPERAND(scan); -+ -+ /* Starting-point info. */ -+ if (OP(scan) == EXACTLY) -+ r->regstart = *OPERAND(scan); -+ else if (OP(scan) == BOL) -+ r->reganch++; -+ -+ /* -+ * If there's something expensive in the r.e., find the -+ * longest literal string that must appear and make it the -+ * regmust. Resolve ties in favor of later strings, since -+ * the regstart check works with the beginning of the r.e. -+ * and avoiding duplication strengthens checking. Not a -+ * strong reason, but sufficient in the absence of others. -+ */ -+ if (flags&SPSTART) { -+ longest = NULL; -+ len = 0; -+ for (; scan != NULL; scan = regnext(&g, scan)) -+ if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) { -+ longest = OPERAND(scan); -+ len = strlen(OPERAND(scan)); -+ } -+ r->regmust = longest; -+ r->regmlen = len; -+ } -+ } -+ -+ return(r); -+} -+ -+/* -+ - reg - regular expression, i.e. main body or parenthesized thing -+ * -+ * Caller must absorb opening parenthesis. -+ * -+ * Combining parenthesis handling with the base level of regular expression -+ * is a trifle forced, but the need to tie the tails of the branches to what -+ * follows makes it hard to avoid. -+ */ -+static char * -+reg(struct match_globals *g, int paren, int *flagp /* Parenthesized? */ ) -+{ -+ register char *ret; -+ register char *br; -+ register char *ender; -+ register int parno = 0; /* 0 makes gcc happy */ -+ int flags; -+ -+ *flagp = HASWIDTH; /* Tentatively. */ -+ -+ /* Make an OPEN node, if parenthesized. */ -+ if (paren) { -+ if (g->regnpar >= NSUBEXP) -+ FAIL("too many ()"); -+ parno = g->regnpar; -+ g->regnpar++; -+ ret = regnode(g, OPEN+parno); -+ } else -+ ret = NULL; -+ -+ /* Pick up the branches, linking them together. */ -+ br = regbranch(g, &flags); -+ if (br == NULL) -+ return(NULL); -+ if (ret != NULL) -+ regtail(g, ret, br); /* OPEN -> first. */ -+ else -+ ret = br; -+ if (!(flags&HASWIDTH)) -+ *flagp &= ~HASWIDTH; -+ *flagp |= flags&SPSTART; -+ while (*g->regparse == '|') { -+ g->regparse++; -+ br = regbranch(g, &flags); -+ if (br == NULL) -+ return(NULL); -+ regtail(g, ret, br); /* BRANCH -> BRANCH. */ -+ if (!(flags&HASWIDTH)) -+ *flagp &= ~HASWIDTH; -+ *flagp |= flags&SPSTART; -+ } -+ -+ /* Make a closing node, and hook it on the end. */ -+ ender = regnode(g, (paren) ? CLOSE+parno : END); -+ regtail(g, ret, ender); -+ -+ /* Hook the tails of the branches to the closing node. */ -+ for (br = ret; br != NULL; br = regnext(g, br)) -+ regoptail(g, br, ender); -+ -+ /* Check for proper termination. */ -+ if (paren && *g->regparse++ != ')') { -+ FAIL("unmatched ()"); -+ } else if (!paren && *g->regparse != '\0') { -+ if (*g->regparse == ')') { -+ FAIL("unmatched ()"); -+ } else -+ FAIL("junk on end"); /* "Can't happen". */ -+ /* NOTREACHED */ -+ } -+ -+ return(ret); -+} -+ -+/* -+ - regbranch - one alternative of an | operator -+ * -+ * Implements the concatenation operator. -+ */ -+static char * -+regbranch(struct match_globals *g, int *flagp) -+{ -+ register char *ret; -+ register char *chain; -+ register char *latest; -+ int flags; -+ -+ *flagp = WORST; /* Tentatively. */ -+ -+ ret = regnode(g, BRANCH); -+ chain = NULL; -+ while (*g->regparse != '\0' && *g->regparse != '|' && *g->regparse != ')') { -+ latest = regpiece(g, &flags); -+ if (latest == NULL) -+ return(NULL); -+ *flagp |= flags&HASWIDTH; -+ if (chain == NULL) /* First piece. */ -+ *flagp |= flags&SPSTART; -+ else -+ regtail(g, chain, latest); -+ chain = latest; -+ } -+ if (chain == NULL) /* Loop ran zero times. */ -+ (void) regnode(g, NOTHING); -+ -+ return(ret); -+} -+ -+/* -+ - regpiece - something followed by possible [*+?] -+ * -+ * Note that the branching code sequences used for ? and the general cases -+ * of * and + are somewhat optimized: they use the same NOTHING node as -+ * both the endmarker for their branch list and the body of the last branch. -+ * It might seem that this node could be dispensed with entirely, but the -+ * endmarker role is not redundant. -+ */ -+static char * -+regpiece(struct match_globals *g, int *flagp) -+{ -+ register char *ret; -+ register char op; -+ register char *next; -+ int flags; -+ -+ ret = regatom(g, &flags); -+ if (ret == NULL) -+ return(NULL); -+ -+ op = *g->regparse; -+ if (!ISMULT(op)) { -+ *flagp = flags; -+ return(ret); -+ } -+ -+ if (!(flags&HASWIDTH) && op != '?') -+ FAIL("*+ operand could be empty"); -+ *flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH); -+ -+ if (op == '*' && (flags&SIMPLE)) -+ reginsert(g, STAR, ret); -+ else if (op == '*') { -+ /* Emit x* as (x&|), where & means "self". */ -+ reginsert(g, BRANCH, ret); /* Either x */ -+ regoptail(g, ret, regnode(g, BACK)); /* and loop */ -+ regoptail(g, ret, ret); /* back */ -+ regtail(g, ret, regnode(g, BRANCH)); /* or */ -+ regtail(g, ret, regnode(g, NOTHING)); /* null. */ -+ } else if (op == '+' && (flags&SIMPLE)) -+ reginsert(g, PLUS, ret); -+ else if (op == '+') { -+ /* Emit x+ as x(&|), where & means "self". */ -+ next = regnode(g, BRANCH); /* Either */ -+ regtail(g, ret, next); -+ regtail(g, regnode(g, BACK), ret); /* loop back */ -+ regtail(g, next, regnode(g, BRANCH)); /* or */ -+ regtail(g, ret, regnode(g, NOTHING)); /* null. */ -+ } else if (op == '?') { -+ /* Emit x? as (x|) */ -+ reginsert(g, BRANCH, ret); /* Either x */ -+ regtail(g, ret, regnode(g, BRANCH)); /* or */ -+ next = regnode(g, NOTHING); /* null. */ -+ regtail(g, ret, next); -+ regoptail(g, ret, next); -+ } -+ g->regparse++; -+ if (ISMULT(*g->regparse)) -+ FAIL("nested *?+"); -+ -+ return(ret); -+} -+ -+/* -+ - regatom - the lowest level -+ * -+ * Optimization: gobbles an entire sequence of ordinary characters so that -+ * it can turn them into a single node, which is smaller to store and -+ * faster to run. Backslashed characters are exceptions, each becoming a -+ * separate node; the code is simpler that way and it's not worth fixing. -+ */ -+static char * -+regatom(struct match_globals *g, int *flagp) -+{ -+ register char *ret; -+ int flags; -+ -+ *flagp = WORST; /* Tentatively. */ -+ -+ switch (*g->regparse++) { -+ case '^': -+ ret = regnode(g, BOL); -+ break; -+ case '$': -+ ret = regnode(g, EOL); -+ break; -+ case '.': -+ ret = regnode(g, ANY); -+ *flagp |= HASWIDTH|SIMPLE; -+ break; -+ case '[': { -+ register int class; -+ register int classend; -+ -+ if (*g->regparse == '^') { /* Complement of range. */ -+ ret = regnode(g, ANYBUT); -+ g->regparse++; -+ } else -+ ret = regnode(g, ANYOF); -+ if (*g->regparse == ']' || *g->regparse == '-') -+ regc(g, *g->regparse++); -+ while (*g->regparse != '\0' && *g->regparse != ']') { -+ if (*g->regparse == '-') { -+ g->regparse++; -+ if (*g->regparse == ']' || *g->regparse == '\0') -+ regc(g, '-'); -+ else { -+ class = UCHARAT(g->regparse-2)+1; -+ classend = UCHARAT(g->regparse); -+ if (class > classend+1) -+ FAIL("invalid [] range"); -+ for (; class <= classend; class++) -+ regc(g, class); -+ g->regparse++; -+ } -+ } else -+ regc(g, *g->regparse++); -+ } -+ regc(g, '\0'); -+ if (*g->regparse != ']') -+ FAIL("unmatched []"); -+ g->regparse++; -+ *flagp |= HASWIDTH|SIMPLE; -+ } -+ break; -+ case '(': -+ ret = reg(g, 1, &flags); -+ if (ret == NULL) -+ return(NULL); -+ *flagp |= flags&(HASWIDTH|SPSTART); -+ break; -+ case '\0': -+ case '|': -+ case ')': -+ FAIL("internal urp"); /* Supposed to be caught earlier. */ -+ break; -+ case '?': -+ case '+': -+ case '*': -+ FAIL("?+* follows nothing"); -+ break; -+ case '\\': -+ if (*g->regparse == '\0') -+ FAIL("trailing \\"); -+ ret = regnode(g, EXACTLY); -+ regc(g, *g->regparse++); -+ regc(g, '\0'); -+ *flagp |= HASWIDTH|SIMPLE; -+ break; -+ default: { -+ register int len; -+ register char ender; -+ -+ g->regparse--; -+ len = my_strcspn((const char *)g->regparse, (const char *)META); -+ if (len <= 0) -+ FAIL("internal disaster"); -+ ender = *(g->regparse+len); -+ if (len > 1 && ISMULT(ender)) -+ len--; /* Back off clear of ?+* operand. */ -+ *flagp |= HASWIDTH; -+ if (len == 1) -+ *flagp |= SIMPLE; -+ ret = regnode(g, EXACTLY); -+ while (len > 0) { -+ regc(g, *g->regparse++); -+ len--; -+ } -+ regc(g, '\0'); -+ } -+ break; -+ } -+ -+ return(ret); -+} -+ -+/* -+ - regnode - emit a node -+ */ -+static char * /* Location. */ -+regnode(struct match_globals *g, char op) -+{ -+ register char *ret; -+ register char *ptr; -+ -+ ret = g->regcode; -+ if (ret == &g->regdummy) { -+ g->regsize += 3; -+ return(ret); -+ } -+ -+ ptr = ret; -+ *ptr++ = op; -+ *ptr++ = '\0'; /* Null "next" pointer. */ -+ *ptr++ = '\0'; -+ g->regcode = ptr; -+ -+ return(ret); -+} -+ -+/* -+ - regc - emit (if appropriate) a byte of code -+ */ -+static void -+regc(struct match_globals *g, char b) -+{ -+ if (g->regcode != &g->regdummy) -+ *g->regcode++ = b; -+ else -+ g->regsize++; -+} -+ -+/* -+ - reginsert - insert an operator in front of already-emitted operand -+ * -+ * Means relocating the operand. -+ */ -+static void -+reginsert(struct match_globals *g, char op, char* opnd) -+{ -+ register char *src; -+ register char *dst; -+ register char *place; -+ -+ if (g->regcode == &g->regdummy) { -+ g->regsize += 3; -+ return; -+ } -+ -+ src = g->regcode; -+ g->regcode += 3; -+ dst = g->regcode; -+ while (src > opnd) -+ *--dst = *--src; -+ -+ place = opnd; /* Op node, where operand used to be. */ -+ *place++ = op; -+ *place++ = '\0'; -+ *place++ = '\0'; -+} -+ -+/* -+ - regtail - set the next-pointer at the end of a node chain -+ */ -+static void -+regtail(struct match_globals *g, char *p, char *val) -+{ -+ register char *scan; -+ register char *temp; -+ register int offset; -+ -+ if (p == &g->regdummy) -+ return; -+ -+ /* Find last node. */ -+ scan = p; -+ for (;;) { -+ temp = regnext(g, scan); -+ if (temp == NULL) -+ break; -+ scan = temp; -+ } -+ -+ if (OP(scan) == BACK) -+ offset = scan - val; -+ else -+ offset = val - scan; -+ *(scan+1) = (offset>>8)&0377; -+ *(scan+2) = offset&0377; -+} -+ -+/* -+ - regoptail - regtail on operand of first argument; nop if operandless -+ */ -+static void -+regoptail(struct match_globals *g, char *p, char *val) -+{ -+ /* "Operandless" and "op != BRANCH" are synonymous in practice. */ -+ if (p == NULL || p == &g->regdummy || OP(p) != BRANCH) -+ return; -+ regtail(g, OPERAND(p), val); -+} -+ -+/* -+ * regexec and friends -+ */ -+ -+ -+/* -+ * Forwards. -+ */ -+STATIC int regtry(struct match_globals *g, regexp *prog, char *string); -+STATIC int regmatch(struct match_globals *g, char *prog); -+STATIC int regrepeat(struct match_globals *g, char *p); -+ -+#ifdef DEBUG -+int regnarrate = 0; -+void regdump(); -+STATIC char *regprop(char *op); -+#endif -+ -+/* -+ - regexec - match a regexp against a string -+ */ -+int -+regexec(regexp *prog, char *string) -+{ -+ register char *s; -+ struct match_globals g; -+ -+ /* Be paranoid... */ -+ if (prog == NULL || string == NULL) { -+ printk("<3>Regexp: NULL parameter\n"); -+ return(0); -+ } -+ -+ /* Check validity of program. */ -+ if (UCHARAT(prog->program) != MAGIC) { -+ printk("<3>Regexp: corrupted program\n"); -+ return(0); -+ } -+ -+ /* If there is a "must appear" string, look for it. */ -+ if (prog->regmust != NULL) { -+ s = string; -+ while ((s = strchr(s, prog->regmust[0])) != NULL) { -+ if (strncmp(s, prog->regmust, prog->regmlen) == 0) -+ break; /* Found it. */ -+ s++; -+ } -+ if (s == NULL) /* Not present. */ -+ return(0); -+ } -+ -+ /* Mark beginning of line for ^ . */ -+ g.regbol = string; -+ -+ /* Simplest case: anchored match need be tried only once. */ -+ if (prog->reganch) -+ return(regtry(&g, prog, string)); -+ -+ /* Messy cases: unanchored match. */ -+ s = string; -+ if (prog->regstart != '\0') -+ /* We know what char it must start with. */ -+ while ((s = strchr(s, prog->regstart)) != NULL) { -+ if (regtry(&g, prog, s)) -+ return(1); -+ s++; -+ } -+ else -+ /* We don't -- general case. */ -+ do { -+ if (regtry(&g, prog, s)) -+ return(1); -+ } while (*s++ != '\0'); -+ -+ /* Failure. */ -+ return(0); -+} -+ -+/* -+ - regtry - try match at specific point -+ */ -+static int /* 0 failure, 1 success */ -+regtry(struct match_globals *g, regexp *prog, char *string) -+{ -+ register int i; -+ register char **sp; -+ register char **ep; -+ -+ g->reginput = string; -+ g->regstartp = prog->startp; -+ g->regendp = prog->endp; -+ -+ sp = prog->startp; -+ ep = prog->endp; -+ for (i = NSUBEXP; i > 0; i--) { -+ *sp++ = NULL; -+ *ep++ = NULL; -+ } -+ if (regmatch(g, prog->program + 1)) { -+ prog->startp[0] = string; -+ prog->endp[0] = g->reginput; -+ return(1); -+ } else -+ return(0); -+} -+ -+/* -+ - regmatch - main matching routine -+ * -+ * Conceptually the strategy is simple: check to see whether the current -+ * node matches, call self recursively to see whether the rest matches, -+ * and then act accordingly. In practice we make some effort to avoid -+ * recursion, in particular by going through "ordinary" nodes (that don't -+ * need to know whether the rest of the match failed) by a loop instead of -+ * by recursion. -+ */ -+static int /* 0 failure, 1 success */ -+regmatch(struct match_globals *g, char *prog) -+{ -+ register char *scan = prog; /* Current node. */ -+ char *next; /* Next node. */ -+ -+#ifdef DEBUG -+ if (scan != NULL && regnarrate) -+ fprintf(stderr, "%s(\n", regprop(scan)); -+#endif -+ while (scan != NULL) { -+#ifdef DEBUG -+ if (regnarrate) -+ fprintf(stderr, "%s...\n", regprop(scan)); -+#endif -+ next = regnext(g, scan); -+ -+ switch (OP(scan)) { -+ case BOL: -+ if (g->reginput != g->regbol) -+ return(0); -+ break; -+ case EOL: -+ if (*g->reginput != '\0') -+ return(0); -+ break; -+ case ANY: -+ if (*g->reginput == '\0') -+ return(0); -+ g->reginput++; -+ break; -+ case EXACTLY: { -+ register int len; -+ register char *opnd; -+ -+ opnd = OPERAND(scan); -+ /* Inline the first character, for speed. */ -+ if (*opnd != *g->reginput) -+ return(0); -+ len = strlen(opnd); -+ if (len > 1 && strncmp(opnd, g->reginput, len) != 0) -+ return(0); -+ g->reginput += len; -+ } -+ break; -+ case ANYOF: -+ if (*g->reginput == '\0' || strchr(OPERAND(scan), *g->reginput) == NULL) -+ return(0); -+ g->reginput++; -+ break; -+ case ANYBUT: -+ if (*g->reginput == '\0' || strchr(OPERAND(scan), *g->reginput) != NULL) -+ return(0); -+ g->reginput++; -+ break; -+ case NOTHING: -+ case BACK: -+ break; -+ case OPEN+1: -+ case OPEN+2: -+ case OPEN+3: -+ case OPEN+4: -+ case OPEN+5: -+ case OPEN+6: -+ case OPEN+7: -+ case OPEN+8: -+ case OPEN+9: { -+ register int no; -+ register char *save; -+ -+ no = OP(scan) - OPEN; -+ save = g->reginput; -+ -+ if (regmatch(g, next)) { -+ /* -+ * Don't set startp if some later -+ * invocation of the same parentheses -+ * already has. -+ */ -+ if (g->regstartp[no] == NULL) -+ g->regstartp[no] = save; -+ return(1); -+ } else -+ return(0); -+ } -+ break; -+ case CLOSE+1: -+ case CLOSE+2: -+ case CLOSE+3: -+ case CLOSE+4: -+ case CLOSE+5: -+ case CLOSE+6: -+ case CLOSE+7: -+ case CLOSE+8: -+ case CLOSE+9: -+ { -+ register int no; -+ register char *save; -+ -+ no = OP(scan) - CLOSE; -+ save = g->reginput; -+ -+ if (regmatch(g, next)) { -+ /* -+ * Don't set endp if some later -+ * invocation of the same parentheses -+ * already has. -+ */ -+ if (g->regendp[no] == NULL) -+ g->regendp[no] = save; -+ return(1); -+ } else -+ return(0); -+ } -+ break; -+ case BRANCH: { -+ register char *save; -+ -+ if (OP(next) != BRANCH) /* No choice. */ -+ next = OPERAND(scan); /* Avoid recursion. */ -+ else { -+ do { -+ save = g->reginput; -+ if (regmatch(g, OPERAND(scan))) -+ return(1); -+ g->reginput = save; -+ scan = regnext(g, scan); -+ } while (scan != NULL && OP(scan) == BRANCH); -+ return(0); -+ /* NOTREACHED */ -+ } -+ } -+ break; -+ case STAR: -+ case PLUS: { -+ register char nextch; -+ register int no; -+ register char *save; -+ register int min; -+ -+ /* -+ * Lookahead to avoid useless match attempts -+ * when we know what character comes next. -+ */ -+ nextch = '\0'; -+ if (OP(next) == EXACTLY) -+ nextch = *OPERAND(next); -+ min = (OP(scan) == STAR) ? 0 : 1; -+ save = g->reginput; -+ no = regrepeat(g, OPERAND(scan)); -+ while (no >= min) { -+ /* If it could work, try it. */ -+ if (nextch == '\0' || *g->reginput == nextch) -+ if (regmatch(g, next)) -+ return(1); -+ /* Couldn't or didn't -- back up. */ -+ no--; -+ g->reginput = save + no; -+ } -+ return(0); -+ } -+ break; -+ case END: -+ return(1); /* Success! */ -+ break; -+ default: -+ printk("<3>Regexp: memory corruption\n"); -+ return(0); -+ break; -+ } -+ -+ scan = next; -+ } -+ -+ /* -+ * We get here only if there's trouble -- normally "case END" is -+ * the terminating point. -+ */ -+ printk("<3>Regexp: corrupted pointers\n"); -+ return(0); -+} -+ -+/* -+ - regrepeat - repeatedly match something simple, report how many -+ */ -+static int -+regrepeat(struct match_globals *g, char *p) -+{ -+ register int count = 0; -+ register char *scan; -+ register char *opnd; -+ -+ scan = g->reginput; -+ opnd = OPERAND(p); -+ switch (OP(p)) { -+ case ANY: -+ count = strlen(scan); -+ scan += count; -+ break; -+ case EXACTLY: -+ while (*opnd == *scan) { -+ count++; -+ scan++; -+ } -+ break; -+ case ANYOF: -+ while (*scan != '\0' && strchr(opnd, *scan) != NULL) { -+ count++; -+ scan++; -+ } -+ break; -+ case ANYBUT: -+ while (*scan != '\0' && strchr(opnd, *scan) == NULL) { -+ count++; -+ scan++; -+ } -+ break; -+ default: /* Oh dear. Called inappropriately. */ -+ printk("<3>Regexp: internal foulup\n"); -+ count = 0; /* Best compromise. */ -+ break; -+ } -+ g->reginput = scan; -+ -+ return(count); -+} -+ -+/* -+ - regnext - dig the "next" pointer out of a node -+ */ -+static char* -+regnext(struct match_globals *g, char *p) -+{ -+ register int offset; -+ -+ if (p == &g->regdummy) -+ return(NULL); -+ -+ offset = NEXT(p); -+ if (offset == 0) -+ return(NULL); -+ -+ if (OP(p) == BACK) -+ return(p-offset); -+ else -+ return(p+offset); -+} -+ -+#ifdef DEBUG -+ -+STATIC char *regprop(); -+ -+/* -+ - regdump - dump a regexp onto stdout in vaguely comprehensible form -+ */ -+void -+regdump(regexp *r) -+{ -+ register char *s; -+ register char op = EXACTLY; /* Arbitrary non-END op. */ -+ register char *next; -+ /* extern char *strchr(); */ -+ -+ -+ s = r->program + 1; -+ while (op != END) { /* While that wasn't END last time... */ -+ op = OP(s); -+ printf("%2d%s", s-r->program, regprop(s)); /* Where, what. */ -+ next = regnext(s); -+ if (next == NULL) /* Next ptr. */ -+ printf("(0)"); -+ else -+ printf("(%d)", (s-r->program)+(next-s)); -+ s += 3; -+ if (op == ANYOF || op == ANYBUT || op == EXACTLY) { -+ /* Literal string, where present. */ -+ while (*s != '\0') { -+ putchar(*s); -+ s++; -+ } -+ s++; -+ } -+ putchar('\n'); -+ } -+ -+ /* Header fields of interest. */ -+ if (r->regstart != '\0') -+ printf("start `%c' ", r->regstart); -+ if (r->reganch) -+ printf("anchored "); -+ if (r->regmust != NULL) -+ printf("must have \"%s\"", r->regmust); -+ printf("\n"); -+} -+ -+/* -+ - regprop - printable representation of opcode -+ */ -+static char * -+regprop(char *op) -+{ -+#define BUFLEN 50 -+ register char *p; -+ static char buf[BUFLEN]; -+ -+ strcpy(buf, ":"); -+ -+ switch (OP(op)) { -+ case BOL: -+ p = "BOL"; -+ break; -+ case EOL: -+ p = "EOL"; -+ break; -+ case ANY: -+ p = "ANY"; -+ break; -+ case ANYOF: -+ p = "ANYOF"; -+ break; -+ case ANYBUT: -+ p = "ANYBUT"; -+ break; -+ case BRANCH: -+ p = "BRANCH"; -+ break; -+ case EXACTLY: -+ p = "EXACTLY"; -+ break; -+ case NOTHING: -+ p = "NOTHING"; -+ break; -+ case BACK: -+ p = "BACK"; -+ break; -+ case END: -+ p = "END"; -+ break; -+ case OPEN+1: -+ case OPEN+2: -+ case OPEN+3: -+ case OPEN+4: -+ case OPEN+5: -+ case OPEN+6: -+ case OPEN+7: -+ case OPEN+8: -+ case OPEN+9: -+ snprintf(buf+strlen(buf),BUFLEN-strlen(buf), "OPEN%d", OP(op)-OPEN); -+ p = NULL; -+ break; -+ case CLOSE+1: -+ case CLOSE+2: -+ case CLOSE+3: -+ case CLOSE+4: -+ case CLOSE+5: -+ case CLOSE+6: -+ case CLOSE+7: -+ case CLOSE+8: -+ case CLOSE+9: -+ snprintf(buf+strlen(buf),BUFLEN-strlen(buf), "CLOSE%d", OP(op)-CLOSE); -+ p = NULL; -+ break; -+ case STAR: -+ p = "STAR"; -+ break; -+ case PLUS: -+ p = "PLUS"; -+ break; -+ default: -+ printk("<3>Regexp: corrupted opcode\n"); -+ break; -+ } -+ if (p != NULL) -+ strncat(buf, p, BUFLEN-strlen(buf)); -+ return(buf); -+} -+#endif -+ -+ ---- /dev/null -+++ b/net/ipv4/netfilter/weburl_deps/regexp.h -@@ -0,0 +1,41 @@ -+/* -+ * Definitions etc. for regexp(3) routines. -+ * -+ * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof], -+ * not the System V one. -+ */ -+ -+#ifndef REGEXP_H -+#define REGEXP_H -+ -+ -+/* -+http://www.opensource.apple.com/darwinsource/10.3/expect-1/expect/expect.h , -+which contains a version of this library, says: -+ -+ * -+ * NSUBEXP must be at least 10, and no greater than 117 or the parser -+ * will not work properly. -+ * -+ -+However, it looks rather like this library is limited to 10. If you think -+otherwise, let us know. -+*/ -+ -+#define NSUBEXP 10 -+typedef struct regexp { -+ char *startp[NSUBEXP]; -+ char *endp[NSUBEXP]; -+ char regstart; /* Internal use only. */ -+ char reganch; /* Internal use only. */ -+ char *regmust; /* Internal use only. */ -+ int regmlen; /* Internal use only. */ -+ char program[1]; /* Unwarranted chumminess with compiler. */ -+} regexp; -+ -+regexp * regcomp(char *exp, int *patternsize); -+int regexec(regexp *prog, char *string); -+void regsub(regexp *prog, char *source, char *dest); -+void regerror(char *s); -+ -+#endif ---- /dev/null -+++ b/net/ipv4/netfilter/weburl_deps/regmagic.h -@@ -0,0 +1,5 @@ -+/* -+ * The first byte of the regexp internal "program" is actually this magic -+ * number; the start node begins in the second byte. -+ */ -+#define MAGIC 0234 ---- /dev/null -+++ b/net/ipv4/netfilter/weburl_deps/regsub.c -@@ -0,0 +1,95 @@ -+/* -+ * regsub -+ * @(#)regsub.c 1.3 of 2 April 86 -+ * -+ * Copyright (c) 1986 by University of Toronto. -+ * Written by Henry Spencer. Not derived from licensed software. -+ * -+ * Permission is granted to anyone to use this software for any -+ * purpose on any computer system, and to redistribute it freely, -+ * subject to the following restrictions: -+ * -+ * 1. The author is not responsible for the consequences of use of -+ * this software, no matter how awful, even if they arise -+ * from defects in it. -+ * -+ * 2. The origin of this software must not be misrepresented, either -+ * by explicit claim or by omission. -+ * -+ * 3. Altered versions must be plainly marked as such, and must not -+ * be misrepresented as being the original software. -+ * -+ * -+ * This code was modified by Ethan Sommer to work within the kernel -+ * (it now uses kmalloc etc..) -+ * -+ */ -+#include "regexp.h" -+#include "regmagic.h" -+#include -+ -+ -+#ifndef CHARBITS -+#define UCHARAT(p) ((int)*(unsigned char *)(p)) -+#else -+#define UCHARAT(p) ((int)*(p)&CHARBITS) -+#endif -+ -+#if 0 -+//void regerror(char * s) -+//{ -+// printk("regexp(3): %s", s); -+// /* NOTREACHED */ -+//} -+#endif -+ -+/* -+ - regsub - perform substitutions after a regexp match -+ */ -+void -+regsub(regexp * prog, char * source, char * dest) -+{ -+ register char *src; -+ register char *dst; -+ register char c; -+ register int no; -+ register int len; -+ -+ /* Not necessary and gcc doesn't like it -MLS */ -+ /*extern char *strncpy();*/ -+ -+ if (prog == NULL || source == NULL || dest == NULL) { -+ regerror("NULL parm to regsub"); -+ return; -+ } -+ if (UCHARAT(prog->program) != MAGIC) { -+ regerror("damaged regexp fed to regsub"); -+ return; -+ } -+ -+ src = source; -+ dst = dest; -+ while ((c = *src++) != '\0') { -+ if (c == '&') -+ no = 0; -+ else if (c == '\\' && '0' <= *src && *src <= '9') -+ no = *src++ - '0'; -+ else -+ no = -1; -+ -+ if (no < 0) { /* Ordinary character. */ -+ if (c == '\\' && (*src == '\\' || *src == '&')) -+ c = *src++; -+ *dst++ = c; -+ } else if (prog->startp[no] != NULL && prog->endp[no] != NULL) { -+ len = prog->endp[no] - prog->startp[no]; -+ (void) strncpy(dst, prog->startp[no], len); -+ dst += len; -+ if (len != 0 && *(dst-1) == '\0') { /* strncpy hit NUL. */ -+ regerror("damaged match string"); -+ return; -+ } -+ } -+ } -+ *dst++ = '\0'; -+} ---- /dev/null -+++ b/net/ipv4/netfilter/weburl_deps/tree_map.h -@@ -0,0 +1,1084 @@ -+/* -+ * Copyright © 2008 by Eric Bishop -+ * -+ * This work 'as-is' we provide. -+ * No warranty, express or implied. -+ * We've done our best, -+ * to debug and test. -+ * Liability for damages denied. -+ * -+ * Permission is granted hereby, -+ * to copy, share, and modify. -+ * Use as is fit, -+ * free or for profit. -+ * On this notice these rights rely. -+ * -+ * -+ * -+ * Note that unlike other portions of Gargoyle this code -+ * does not fall under the GPL, but the rather whimsical -+ * 'Poetic License' above. -+ * -+ * Basically, this library contains a bunch of utilities -+ * that I find useful. I'm sure other libraries exist -+ * that are just as good or better, but I like these tools -+ * because I personally wrote them, so I know their quirks. -+ * (i.e. I know where the bodies are buried). I want to -+ * make sure that I can re-use these utilities for whatever -+ * code I may want to write in the future be it -+ * proprietary or open-source, so I've put them under -+ * a very, very permissive license. -+ * -+ * If you find this code useful, use it. If not, don't. -+ * I really don't care. -+ * -+ */ -+ -+ -+#if __KERNEL__ -+ #define malloc(foo) kmalloc(foo,GFP_ATOMIC) -+ #define free(foo) kfree(foo) -+ #define printf(format,args...) printk(format,##args) -+ -+ /* kernel strdup */ -+ static inline char *kernel_strdup(const char *str); -+ static inline char *kernel_strdup(const char *str) -+ { -+ char *tmp; -+ long int s; -+ s=strlen(str) + 1; -+ tmp = kmalloc(s, GFP_ATOMIC); -+ if (tmp != NULL) -+ { -+ memcpy(tmp, str, s); -+ } -+ return tmp; -+ } -+ #define strdup kernel_strdup -+ -+#endif -+ -+ -+ -+/* tree_map structs / prototypes */ -+typedef struct long_tree_map_node -+{ -+ unsigned long key; -+ void* value; -+ -+ signed char balance; -+ struct long_tree_map_node* left; -+ struct long_tree_map_node* right; -+} long_map_node; -+ -+typedef struct -+{ -+ long_map_node* root; -+ unsigned long num_elements; -+ -+}long_map; -+ -+typedef struct -+{ -+ long_map lm; -+ unsigned char store_keys; -+ unsigned long num_elements; -+ -+}string_map; -+ -+ -+ -+/* long map functions */ -+long_map* initialize_long_map(void); -+void* get_long_map_element(long_map* map, unsigned long key); -+void* get_smallest_long_map_element(long_map* map, unsigned long* smallest_key); -+void* get_largest_long_map_element(long_map* map, unsigned long* largest_key); -+void* remove_smallest_long_map_element(long_map* map, unsigned long* smallest_key); -+void* remove_largest_long_map_element(long_map* map, unsigned long* largest_key); -+void* set_long_map_element(long_map* map, unsigned long key, void* value); -+void* remove_long_map_element(long_map* map, unsigned long key); -+unsigned long* get_sorted_long_map_keys(long_map* map, unsigned long* num_keys_returned); -+void** get_sorted_long_map_values(long_map* map, unsigned long* num_values_returned); -+void** destroy_long_map(long_map* map, int destruction_type, unsigned long* num_destroyed); -+void apply_to_every_long_map_value(long_map* map, void (*apply_func)(unsigned long key, void* value)); -+ -+/* string map functions */ -+string_map* initialize_string_map(unsigned char store_keys); -+void* get_string_map_element(string_map* map, const char* key); -+void* set_string_map_element(string_map* map, const char* key, void* value); -+void* remove_string_map_element(string_map* map, const char* key); -+char** get_string_map_keys(string_map* map, unsigned long* num_keys_returned); -+void** get_string_map_values(string_map* map, unsigned long* num_values_returned); -+void** destroy_string_map(string_map* map, int destruction_type, unsigned long* num_destroyed); -+void apply_to_every_string_map_value(string_map* map, void (*apply_func)(char* key, void* value)); -+ -+ -+/* -+ * three different ways to deal with values when data structure is destroyed -+ */ -+#define DESTROY_MODE_RETURN_VALUES 20 -+#define DESTROY_MODE_FREE_VALUES 21 -+#define DESTROY_MODE_IGNORE_VALUES 22 -+ -+ -+/* -+ * for convenience & backwards compatibility alias _string_map_ functions to -+ * _map_ functions since string map is used more often than long map -+ */ -+#define initialize_map initialize_string_map -+#define set_map_element set_string_map_element -+#define get_map_element get_string_map_element -+#define remove_map_element remove_string_map_element -+#define get_map_keys get_string_map_keys -+#define get_map_values get_string_map_values -+#define destroy_map destroy_string_map -+ -+ -+/* internal utility structures/ functions */ -+typedef struct stack_node_struct -+{ -+ long_map_node** node_ptr; -+ signed char direction; -+ struct stack_node_struct* previous; -+} stack_node; -+ -+static void free_stack(stack_node* stack); -+static void** destroy_long_map_values(long_map* map, int destruction_type, unsigned long* num_destroyed); -+static void apply_to_every_long_map_node(long_map_node* node, void (*apply_func)(unsigned long key, void* value)); -+static void apply_to_every_string_map_node(long_map_node* node, unsigned char has_key, void (*apply_func)(char* key, void* value)); -+static void get_sorted_node_keys(long_map_node* node, unsigned long* key_list, unsigned long* next_key_index, int depth); -+static void get_sorted_node_values(long_map_node* node, void** value_list, unsigned long* next_value_index, int depth); -+static signed char rebalance (long_map_node** n, signed char direction, signed char update_op); -+static void rotate_right (long_map_node** parent); -+static void rotate_left (long_map_node** parent); -+ -+/* internal for string map */ -+typedef struct -+{ -+ char* key; -+ void* value; -+} string_map_key_value; -+static unsigned long sdbm_string_hash(const char *key); -+ -+ -+ -+ -+/*************************************************** -+ * For testing only -+ ***************************************************/ -+/* -+void print_list(stack_node *l); -+ -+void print_list(stack_node *l) -+{ -+ if(l != NULL) -+ { -+ printf(" list key = %ld, dir=%d, \n", (*(l->node_ptr))->key, l->direction); -+ print_list(l->previous); -+ } -+} -+*/ -+/****************************************************** -+ * End testing Code -+ *******************************************************/ -+ -+ -+ -+ -+/*************************************************** -+ * string_map function definitions -+ ***************************************************/ -+ -+string_map* initialize_string_map(unsigned char store_keys) -+{ -+ string_map* map = (string_map*)malloc(sizeof(string_map)); -+ if(map != NULL) -+ { -+ map->store_keys = store_keys; -+ map->lm.root = NULL; -+ map->lm.num_elements = 0; -+ map->num_elements = map->lm.num_elements; -+ } -+ return map; -+} -+ -+void* get_string_map_element(string_map* map, const char* key) -+{ -+ unsigned long hashed_key = sdbm_string_hash(key); -+ void* return_value = get_long_map_element( &(map->lm), hashed_key); -+ if(return_value != NULL && map->store_keys) -+ { -+ string_map_key_value* r = (string_map_key_value*)return_value; -+ return_value = r->value; -+ } -+ map->num_elements = map->lm.num_elements; -+ return return_value; -+} -+ -+void* set_string_map_element(string_map* map, const char* key, void* value) -+{ -+ unsigned long hashed_key = sdbm_string_hash(key); -+ void* return_value = NULL; -+ if(map->store_keys) -+ { -+ string_map_key_value* kv = (string_map_key_value*)malloc(sizeof(string_map_key_value)); -+ if(kv == NULL) /* deal with malloc failure */ -+ { -+ return NULL; -+ } -+ kv->key = strdup(key); -+ if(kv->key == NULL) /* deal with malloc failure */ -+ { -+ free(kv); -+ return NULL; -+ } -+ kv->value = value; -+ return_value = set_long_map_element( &(map->lm), hashed_key, kv); -+ if(return_value != NULL) -+ { -+ string_map_key_value* r = (string_map_key_value*)return_value; -+ return_value = r->value; -+ free(r->key); -+ free(r); -+ } -+ } -+ else -+ { -+ return_value = set_long_map_element( &(map->lm), hashed_key, value); -+ } -+ map->num_elements = map->lm.num_elements; -+ return return_value; -+} -+ -+void* remove_string_map_element(string_map* map, const char* key) -+{ -+ unsigned long hashed_key = sdbm_string_hash(key); -+ void* return_value = remove_long_map_element( &(map->lm), hashed_key); -+ -+ if(return_value != NULL && map->store_keys) -+ { -+ string_map_key_value* r = (string_map_key_value*)return_value; -+ return_value = r->value; -+ free(r->key); -+ free(r); -+ } -+ map->num_elements = map->lm.num_elements; -+ return return_value; -+} -+ -+char** get_string_map_keys(string_map* map, unsigned long* num_keys_returned) -+{ -+ char** str_keys; -+ str_keys = (char**)malloc((map->num_elements+1)*sizeof(char*)); -+ if(str_keys == NULL) /* deal with malloc failure */ -+ { -+ return NULL; -+ } -+ str_keys[0] = NULL; -+ *num_keys_returned = 0; -+ if(map->store_keys && map->num_elements > 0) -+ { -+ unsigned long list_length; -+ void** long_values = get_sorted_long_map_values( &(map->lm), &list_length); -+ unsigned long key_index; -+ /*list_length will be 0 on malloc failure in get_sorted_long_map_values, so this code shouldn't seg fault if that happens */ -+ for(key_index = 0; key_index < list_length; key_index++) -+ { -+ str_keys[key_index] = strdup( ((string_map_key_value*)(long_values[key_index]))->key); -+ if(str_keys[key_index] == NULL) /* deal with malloc failure */ -+ { -+ //just return the incomplete list (hey, it's null terminated...) -+ free(long_values); -+ return str_keys; -+ } -+ *num_keys_returned = *num_keys_returned + 1; -+ } -+ str_keys[list_length] = NULL; -+ free(long_values); -+ } -+ return str_keys; -+} -+ -+ -+void** get_string_map_values(string_map* map, unsigned long* num_values_returned) -+{ -+ void** values = NULL; -+ if(map != NULL) -+ { -+ values = get_sorted_long_map_values ( &(map->lm), num_values_returned ); -+ } -+ return values; -+} -+ -+ -+void** destroy_string_map(string_map* map, int destruction_type, unsigned long* num_destroyed) -+{ -+ void** return_values = NULL; -+ if(map != NULL) -+ { -+ if(map->store_keys) -+ { -+ void** kvs = destroy_long_map_values( &(map->lm), DESTROY_MODE_RETURN_VALUES, num_destroyed ); -+ unsigned long kv_index = 0; -+ for(kv_index=0; kv_index < *num_destroyed; kv_index++) -+ { -+ string_map_key_value* kv = (string_map_key_value*)kvs[kv_index]; -+ void* value = kv->value; -+ -+ free(kv->key); -+ free(kv); -+ if(destruction_type == DESTROY_MODE_FREE_VALUES) -+ { -+ free(value); -+ } -+ if(destruction_type == DESTROY_MODE_RETURN_VALUES) -+ { -+ kvs[kv_index] = value; -+ } -+ } -+ if(destruction_type == DESTROY_MODE_RETURN_VALUES) -+ { -+ return_values = kvs; -+ } -+ else -+ { -+ free(kvs); -+ } -+ } -+ else -+ { -+ return_values = destroy_long_map_values( &(map->lm), destruction_type, num_destroyed ); -+ } -+ free(map); -+ } -+ return return_values; -+} -+ -+ -+ -+ -+/*************************************************** -+ * long_map function definitions -+ ***************************************************/ -+ -+long_map* initialize_long_map(void) -+{ -+ long_map* map = (long_map*)malloc(sizeof(long_map)); -+ if(map != NULL) /* test for malloc failure */ -+ { -+ map->root = NULL; -+ map->num_elements = 0; -+ } -+ return map; -+} -+ -+void* get_long_map_element(long_map* map, unsigned long key) -+{ -+ void* value = NULL; -+ -+ if(map->root != NULL) -+ { -+ long_map_node* parent_node = map->root; -+ long_map_node* next_node; -+ while( key != parent_node->key && (next_node = (long_map_node *)(key < parent_node->key ? parent_node->left : parent_node->right)) != NULL) -+ { -+ parent_node = next_node; -+ } -+ if(parent_node->key == key) -+ { -+ value = parent_node->value; -+ } -+ } -+ return value; -+} -+ -+void* get_smallest_long_map_element(long_map* map, unsigned long* smallest_key) -+{ -+ void* value = NULL; -+ if(map->root != NULL) -+ { -+ long_map_node* next_node = map->root; -+ while( next_node->left != NULL) -+ { -+ next_node = next_node->left; -+ } -+ value = next_node->value; -+ *smallest_key = next_node->key; -+ } -+ return value; -+} -+ -+void* get_largest_long_map_element(long_map* map, unsigned long* largest_key) -+{ -+ void* value = NULL; -+ if(map->root != NULL) -+ { -+ long_map_node* next_node = map->root; -+ while( next_node->right != NULL) -+ { -+ next_node = next_node->right; -+ } -+ value = next_node->value; -+ *largest_key = next_node->key; -+ } -+ return value; -+} -+ -+void* remove_smallest_long_map_element(long_map* map, unsigned long* smallest_key) -+{ -+ get_smallest_long_map_element(map, smallest_key); -+ return remove_long_map_element(map, *smallest_key); -+} -+ -+void* remove_largest_long_map_element(long_map* map, unsigned long* largest_key) -+{ -+ get_largest_long_map_element(map, largest_key); -+ return remove_long_map_element(map, *largest_key); -+} -+ -+ -+/* if replacement performed, returns replaced value, otherwise null */ -+void* set_long_map_element(long_map* map, unsigned long key, void* value) -+{ -+ stack_node* parent_list = NULL; -+ void* old_value = NULL; -+ int old_value_found = 0; -+ -+ long_map_node* parent_node; -+ long_map_node* next_node; -+ stack_node* next_parent; -+ stack_node* previous_parent; -+ signed char new_balance; -+ -+ -+ long_map_node* new_node = (long_map_node*)malloc(sizeof(long_map_node)); -+ if(new_node == NULL) -+ { -+ return NULL; -+ } -+ new_node->value = value; -+ new_node->key = key; -+ new_node->left = NULL; -+ new_node->right = NULL; -+ new_node->balance = 0; -+ -+ -+ -+ if(map->root == NULL) -+ { -+ map->root = new_node; -+ } -+ else -+ { -+ parent_node = map->root; -+ -+ next_parent = (stack_node*)malloc(sizeof(stack_node)); -+ if(next_parent == NULL) /* deal with malloc failure */ -+ { -+ free(new_node); -+ return NULL; /* won't insert but won't seg fault */ -+ } -+ next_parent->node_ptr = &(map->root); -+ next_parent->previous = parent_list; -+ parent_list = next_parent; -+ -+ while( key != parent_node->key && (next_node = (key < parent_node->key ? parent_node->left : parent_node->right) ) != NULL) -+ { -+ next_parent = (stack_node*)malloc(sizeof(stack_node)); -+ if(next_parent == NULL) /* deal with malloc failure */ -+ { -+ /* free previous stack nodes to prevent memory leak */ -+ free_stack(parent_list); -+ free(new_node); -+ return NULL; -+ } -+ next_parent->node_ptr = key < parent_node->key ? &(parent_node->left) : &(parent_node->right); -+ next_parent->previous = parent_list; -+ next_parent->previous->direction = key < parent_node->key ? -1 : 1; -+ parent_list = next_parent; -+ -+ parent_node = next_node; -+ } -+ -+ -+ if(key == parent_node->key) -+ { -+ old_value = parent_node->value; -+ old_value_found = 1; -+ parent_node->value = value; -+ free(new_node); -+ /* we merely replaced a node, no need to rebalance */ -+ } -+ else -+ { -+ if(key < parent_node->key) -+ { -+ parent_node->left = (void*)new_node; -+ parent_list->direction = -1; -+ } -+ else -+ { -+ parent_node->right = (void*)new_node; -+ parent_list->direction = 1; -+ } -+ -+ -+ /* we inserted a node, rebalance */ -+ previous_parent = parent_list; -+ new_balance = 1; /* initial value is not used, but must not be 0 for initial loop condition */ -+ -+ -+ while(previous_parent != NULL && new_balance != 0) -+ { -+ new_balance = rebalance(previous_parent->node_ptr, previous_parent->direction, 1); -+ previous_parent = previous_parent->previous; -+ } -+ } -+ } -+ -+ free_stack(parent_list); -+ -+ if(old_value_found == 0) -+ { -+ map->num_elements = map->num_elements + 1; -+ } -+ -+ return old_value; -+} -+ -+ -+void* remove_long_map_element(long_map* map, unsigned long key) -+{ -+ -+ void* value = NULL; -+ -+ long_map_node* root_node = map->root; -+ stack_node* parent_list = NULL; -+ -+ -+ long_map_node* remove_parent; -+ long_map_node* remove_node; -+ long_map_node* next_node; -+ -+ long_map_node* replacement; -+ long_map_node* replacement_parent; -+ long_map_node* replacement_next; -+ -+ stack_node* next_parent; -+ stack_node* previous_parent; -+ stack_node* replacement_stack_node; -+ -+ -+ signed char new_balance; -+ -+ -+ -+ if(root_node != NULL) -+ { -+ remove_parent = root_node; -+ remove_node = key < remove_parent->key ? remove_parent->left : remove_parent->right; -+ -+ if(remove_node != NULL && key != remove_parent->key) -+ { -+ next_parent = (stack_node*)malloc(sizeof(stack_node)); -+ if(next_parent == NULL) /* deal with malloc failure */ -+ { -+ return NULL; -+ } -+ next_parent->node_ptr = &(map->root); -+ next_parent->previous = parent_list; -+ parent_list = next_parent; -+ while( key != remove_node->key && (next_node = (key < remove_node->key ? remove_node->left : remove_node->right)) != NULL) -+ { -+ next_parent = (stack_node*)malloc(sizeof(stack_node)); -+ if(next_parent == NULL) /* deal with malloc failure */ -+ { -+ /* free previous stack nodes to prevent memory leak */ -+ free_stack(parent_list); -+ return NULL; -+ } -+ next_parent->node_ptr = key < remove_parent->key ? &(remove_parent->left) : &(remove_parent->right); -+ next_parent->previous = parent_list; -+ next_parent->previous->direction = key < remove_parent->key ? -1 : 1; -+ parent_list = next_parent; -+ -+ -+ remove_parent = remove_node; -+ remove_node = next_node; -+ } -+ parent_list->direction = key < remove_parent-> key ? -1 : 1; -+ } -+ else -+ { -+ remove_node = remove_parent; -+ } -+ -+ -+ if(key == remove_node->key) -+ { -+ -+ /* find replacement for node we are deleting */ -+ if( remove_node->right == NULL ) -+ { -+ replacement = remove_node->left; -+ } -+ else if( remove_node->right->left == NULL) -+ { -+ -+ replacement = remove_node->right; -+ replacement->left = remove_node->left; -+ replacement->balance = remove_node->balance; -+ -+ /* put pointer to replacement node into list for balance update */ -+ replacement_stack_node = (stack_node*)malloc(sizeof(stack_node)); -+ if(replacement_stack_node == NULL) /* deal with malloc failure */ -+ { -+ /* free previous stack nodes to prevent memory leak */ -+ free_stack(parent_list); -+ return NULL; -+ } -+ replacement_stack_node->previous = parent_list; -+ replacement_stack_node->direction = 1; /* replacement is from right */ -+ if(remove_node == remove_parent) /* special case for root node */ -+ { -+ replacement_stack_node->node_ptr = &(map->root); -+ } -+ else -+ { -+ replacement_stack_node->node_ptr = key < remove_parent-> key ? &(remove_parent->left) : &(remove_parent->right); -+ } -+ parent_list = replacement_stack_node; -+ -+ } -+ else -+ { -+ /* put pointer to replacement node into list for balance update */ -+ replacement_stack_node = (stack_node*)malloc(sizeof(stack_node)); -+ if(replacement_stack_node == NULL) /* deal with malloc failure */ -+ { -+ /* free previous stack nodes to prevent memory leak */ -+ free_stack(parent_list); -+ return NULL; -+ } -+ -+ replacement_stack_node->previous = parent_list; -+ replacement_stack_node->direction = 1; /* we always look for replacement on right */ -+ if(remove_node == remove_parent) /* special case for root node */ -+ { -+ replacement_stack_node->node_ptr = &(map->root); -+ } -+ else -+ { -+ replacement_stack_node->node_ptr = key < remove_parent-> key ? &(remove_parent->left) : &(remove_parent->right); -+ } -+ -+ parent_list = replacement_stack_node; -+ -+ -+ /* -+ * put pointer to replacement node->right into list for balance update -+ * this node will have to be updated with the proper pointer -+ * after we have identified the replacement -+ */ -+ replacement_stack_node = (stack_node*)malloc(sizeof(stack_node)); -+ if(replacement_stack_node == NULL) /* deal with malloc failure */ -+ { -+ /* free previous stack nodes to prevent memory leak */ -+ free_stack(parent_list); -+ return NULL; -+ } -+ -+ replacement_stack_node->previous = parent_list; -+ replacement_stack_node->direction = -1; /* we always look for replacement to left of this node */ -+ parent_list = replacement_stack_node; -+ -+ /* find smallest node on right (large) side of tree */ -+ replacement_parent = remove_node->right; -+ replacement = replacement_parent->left; -+ -+ while((replacement_next = replacement->left) != NULL) -+ { -+ next_parent = (stack_node*)malloc(sizeof(stack_node)); -+ if(next_parent == NULL) /* deal with malloc failure */ -+ { -+ /* free previous stack nodes to prevent memory leak */ -+ free_stack(parent_list); -+ return NULL; -+ } -+ -+ next_parent->node_ptr = &(replacement_parent->left); -+ next_parent->previous = parent_list; -+ next_parent->direction = -1; /* we always go left */ -+ parent_list = next_parent; -+ -+ replacement_parent = replacement; -+ replacement = replacement_next; -+ -+ } -+ -+ replacement_parent->left = replacement->right; -+ -+ replacement->left = remove_node->left; -+ replacement->right = remove_node->right; -+ replacement->balance = remove_node->balance; -+ replacement_stack_node->node_ptr = &(replacement->right); -+ } -+ -+ /* insert replacement at proper location in tree */ -+ if(remove_node == remove_parent) -+ { -+ map->root = replacement; -+ } -+ else -+ { -+ remove_parent->left = remove_node == remove_parent->left ? replacement : remove_parent->left; -+ remove_parent->right = remove_node == remove_parent->right ? replacement : remove_parent->right; -+ } -+ -+ -+ /* rebalance tree */ -+ previous_parent = parent_list; -+ new_balance = 0; -+ while(previous_parent != NULL && new_balance == 0) -+ { -+ new_balance = rebalance(previous_parent->node_ptr, previous_parent->direction, -1); -+ previous_parent = previous_parent->previous; -+ } -+ -+ -+ -+ -+ /* -+ * since we found a value to remove, decrease number of elements in map -+ * set return value to the deleted node's value and free the node -+ */ -+ map->num_elements = map->num_elements - 1; -+ value = remove_node->value; -+ free(remove_node); -+ } -+ } -+ -+ free_stack(parent_list); -+ -+ return value; -+} -+ -+ -+/* note: returned keys are dynamically allocated, you need to free them! */ -+unsigned long* get_sorted_long_map_keys(long_map* map, unsigned long* num_keys_returned) -+{ -+ unsigned long* key_list = (unsigned long*)malloc((map->num_elements)*sizeof(unsigned long)); -+ unsigned long next_key_index; -+ if(key_list == NULL) -+ { -+ *num_keys_returned = 0; -+ return NULL; -+ } -+ next_key_index = 0; -+ get_sorted_node_keys(map->root, key_list, &next_key_index, 0); -+ -+ *num_keys_returned = map->num_elements; -+ -+ return key_list; -+} -+ -+ -+void** get_sorted_long_map_values(long_map* map, unsigned long* num_values_returned) -+{ -+ void** value_list = (void**)malloc((map->num_elements+1)*sizeof(void*)); -+ unsigned long next_value_index; -+ -+ if(value_list == NULL) -+ { -+ *num_values_returned = 0; -+ return NULL; -+ } -+ next_value_index = 0; -+ get_sorted_node_values(map->root, value_list, &next_value_index, 0); -+ value_list[map->num_elements] = NULL; /* since we're dealing with pointers make list null terminated */ -+ -+ *num_values_returned = map->num_elements; -+ return value_list; -+ -+} -+ -+ -+ -+void** destroy_long_map(long_map* map, int destruction_type, unsigned long* num_destroyed) -+{ -+ void** return_values = destroy_long_map_values(map, destruction_type, num_destroyed); -+ free(map); -+ return return_values; -+} -+ -+ -+ -+void apply_to_every_long_map_value(long_map* map, void (*apply_func)(unsigned long key, void* value)) -+{ -+ apply_to_every_long_map_node(map->root, apply_func); -+} -+void apply_to_every_string_map_value(string_map* map, void (*apply_func)(char* key, void* value)) -+{ -+ apply_to_every_string_map_node( (map->lm).root, map->store_keys, apply_func); -+} -+ -+ -+/*************************************************** -+ * internal utility function definitions -+ ***************************************************/ -+static void free_stack(stack_node* stack) -+{ -+ while(stack != NULL) -+ { -+ stack_node* prev_node = stack; -+ stack = prev_node->previous; -+ free(prev_node); -+ } -+ -+} -+ -+static void** destroy_long_map_values(long_map* map, int destruction_type, unsigned long* num_destroyed) -+{ -+ void** return_values = NULL; -+ unsigned long return_index = 0; -+ -+ *num_destroyed = 0; -+ -+ if(destruction_type == DESTROY_MODE_RETURN_VALUES) -+ { -+ return_values = (void**)malloc((map->num_elements+1)*sizeof(void*)); -+ if(return_values == NULL) /* deal with malloc failure */ -+ { -+ destruction_type = DESTROY_MODE_IGNORE_VALUES; /* could cause memory leak, but there's no other way to be sure we won't seg fault */ -+ } -+ else -+ { -+ return_values[map->num_elements] = NULL; -+ } -+ } -+ while(map->num_elements > 0) -+ { -+ unsigned long smallest_key; -+ void* removed_value = remove_smallest_long_map_element(map, &smallest_key); -+ if(destruction_type == DESTROY_MODE_RETURN_VALUES) -+ { -+ return_values[return_index] = removed_value; -+ } -+ if(destruction_type == DESTROY_MODE_FREE_VALUES) -+ { -+ free(removed_value); -+ } -+ return_index++; -+ *num_destroyed = *num_destroyed + 1; -+ } -+ return return_values; -+} -+ -+static void apply_to_every_long_map_node(long_map_node* node, void (*apply_func)(unsigned long key, void* value)) -+{ -+ if(node != NULL) -+ { -+ apply_to_every_long_map_node(node->left, apply_func); -+ -+ apply_func(node->key, node->value); -+ -+ apply_to_every_long_map_node(node->right, apply_func); -+ } -+} -+static void apply_to_every_string_map_node(long_map_node* node, unsigned char has_key, void (*apply_func)(char* key, void* value)) -+{ -+ if(node != NULL) -+ { -+ apply_to_every_string_map_node(node->left, has_key, apply_func); -+ -+ if(has_key) -+ { -+ string_map_key_value* kv = (string_map_key_value*)(node->value); -+ apply_func(kv->key, kv->value); -+ } -+ else -+ { -+ apply_func(NULL, node->value); -+ } -+ apply_to_every_string_map_node(node->right, has_key, apply_func); -+ } -+} -+ -+ -+ -+static void get_sorted_node_keys(long_map_node* node, unsigned long* key_list, unsigned long* next_key_index, int depth) -+{ -+ if(node != NULL) -+ { -+ get_sorted_node_keys(node->left, key_list, next_key_index, depth+1); -+ -+ key_list[ *next_key_index ] = node->key; -+ (*next_key_index)++; -+ -+ get_sorted_node_keys(node->right, key_list, next_key_index, depth+1); -+ } -+} -+ -+static void get_sorted_node_values(long_map_node* node, void** value_list, unsigned long* next_value_index, int depth) -+{ -+ if(node != NULL) -+ { -+ get_sorted_node_values(node->left, value_list, next_value_index, depth+1); -+ -+ value_list[ *next_value_index ] = node->value; -+ (*next_value_index)++; -+ -+ get_sorted_node_values(node->right, value_list, next_value_index, depth+1); -+ } -+} -+ -+ -+ -+/* -+ * direction = -1 indicates left subtree updated, direction = 1 for right subtree -+ * update_op = -1 indicates delete node, update_op = 1 for insert node -+ */ -+static signed char rebalance (long_map_node** n, signed char direction, signed char update_op) -+{ -+ /* -+ printf( "original: key = %ld, balance = %d, update_op=%d, direction=%d\n", (*n)->key, (*n)->balance, update_op, direction); -+ */ -+ -+ (*n)->balance = (*n)->balance + (update_op*direction); -+ -+ if( (*n)->balance < -1) -+ { -+ if((*n)->left->balance < 0) -+ { -+ rotate_right(n); -+ (*n)->right->balance = 0; -+ (*n)->balance = 0; -+ } -+ else if((*n)->left->balance == 0) -+ { -+ rotate_right(n); -+ (*n)->right->balance = -1; -+ (*n)->balance = 1; -+ } -+ else if((*n)->left->balance > 0) -+ { -+ rotate_left( &((*n)->left) ); -+ rotate_right(n); -+ /* -+ if( (*n)->balance < 0 ) -+ { -+ (*n)->left->balance = 0; -+ (*n)->right->balance = 1; -+ } -+ else if( (*n)->balance == 0 ) -+ { -+ (*n)->left->balance = 0; -+ (*n)->right->balance = 0; -+ } -+ else if( (*n)->balance > 0 ) -+ { -+ (*n)->left->balance = -1; -+ (*n)->right->balance = 0; -+ } -+ */ -+ (*n)->left->balance = (*n)->balance > 0 ? -1 : 0; -+ (*n)->right->balance = (*n)->balance < 0 ? 1 : 0; -+ (*n)->balance = 0; -+ } -+ } -+ if( (*n)->balance > 1) -+ { -+ if((*n)->right->balance > 0) -+ { -+ rotate_left(n); -+ (*n)->left->balance = 0; -+ (*n)->balance = 0; -+ } -+ else if ((*n)->right->balance == 0) -+ { -+ rotate_left(n); -+ (*n)->left->balance = 1; -+ (*n)->balance = -1; -+ } -+ else if((*n)->right->balance < 0) -+ { -+ rotate_right( &((*n)->right) ); -+ rotate_left(n); -+ /* -+ if( (*n)->balance < 0 ) -+ { -+ (*n)->left->balance = 0; -+ (*n)->right->balance = 1; -+ } -+ else if( (*n)->balance == 0 ) -+ { -+ (*n)->left->balance = 0; -+ (*n)->right->balance = 0; -+ } -+ else if( (*n)->balance > 0 ) -+ { -+ (*n)->left->balance = -1; -+ (*n)->right->balance = 0; -+ } -+ */ -+ (*n)->left->balance = (*n)->balance > 0 ? -1 : 0; -+ (*n)->right->balance = (*n)->balance < 0 ? 1 : 0; -+ (*n)->balance = 0; -+ } -+ } -+ -+ /* -+ printf( "key = %ld, balance = %d\n", (*n)->key, (*n)->balance); -+ */ -+ -+ return (*n)->balance; -+} -+ -+ -+static void rotate_right (long_map_node** parent) -+{ -+ long_map_node* old_parent = *parent; -+ long_map_node* pivot = old_parent->left; -+ old_parent->left = pivot->right; -+ pivot->right = old_parent; -+ -+ *parent = pivot; -+} -+ -+static void rotate_left (long_map_node** parent) -+{ -+ long_map_node* old_parent = *parent; -+ long_map_node* pivot = old_parent->right; -+ old_parent->right = pivot->left; -+ pivot->left = old_parent; -+ -+ *parent = pivot; -+} -+ -+ -+ -+/*************************************************************************** -+ * This algorithm was created for the sdbm database library (a public-domain -+ * reimplementation of ndbm) and seems to work relatively well in -+ * scrambling bits -+ * -+ * -+ * This code was derived from code found at: -+ * http://www.cse.yorku.ca/~oz/hash.html -+ ***************************************************************************/ -+static unsigned long sdbm_string_hash(const char *key) -+{ -+ unsigned long hashed_key = 0; -+ -+ int index = 0; -+ unsigned int nextch; -+ while(key[index] != '\0') -+ { -+ nextch = key[index]; -+ hashed_key = nextch + (hashed_key << 6) + (hashed_key << 16) - hashed_key; -+ index++; -+ } -+ return hashed_key; -+} -+ -+ diff --git a/target/linux/generic/pending-4.14/760-net-usb-aqc111-Driver-skeleton-for-Aquantia-AQtionUSB-to-5GbE.patch b/target/linux/generic/pending-4.14/760-net-usb-aqc111-Driver-skeleton-for-Aquantia-AQtionUSB-to-5GbE.patch deleted file mode 100644 index b20a129391..0000000000 --- a/target/linux/generic/pending-4.14/760-net-usb-aqc111-Driver-skeleton-for-Aquantia-AQtionUSB-to-5GbE.patch +++ /dev/null @@ -1,1889 +0,0 @@ ---- a/drivers/net/usb/Kconfig -+++ b/drivers/net/usb/Kconfig -@@ -613,4 +613,16 @@ config USB_NET_CH9200 - To compile this driver as a module, choose M here: the - module will be called ch9200. - -+config USB_NET_AQC111 -+ tristate "Aquantia AQtion USB to 5/2.5GbE Controllers support" -+ depends on USB_USBNET -+ select CRC32 -+ default y -+ help -+ This option adds support for Aquantia AQtion USB -+ Ethernet adapters based on AQC111U/AQC112 chips. -+ -+ This driver should work with at least the following devices: -+ * Aquantia AQtion USB to 5GbE -+ - endif # USB_NET_DRIVERS ---- a/drivers/net/usb/Makefile -+++ b/drivers/net/usb/Makefile -@@ -40,3 +40,4 @@ obj-$(CONFIG_USB_VL600) += lg-vl600.o - obj-$(CONFIG_USB_NET_QMI_WWAN) += qmi_wwan.o - obj-$(CONFIG_USB_NET_CDC_MBIM) += cdc_mbim.o - obj-$(CONFIG_USB_NET_CH9200) += ch9200.o -+obj-$(CONFIG_USB_NET_AQC111) += aqc111.o ---- /dev/null -+++ b/drivers/net/usb/aqc111.c -@@ -0,0 +1,1489 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* Aquantia Corp. Aquantia AQtion USB to 5GbE Controller -+ * Copyright (C) 2003-2005 David Hollis -+ * Copyright (C) 2005 Phil Chang -+ * Copyright (C) 2002-2003 TiVo Inc. -+ * Copyright (C) 2017-2018 ASIX -+ * Copyright (C) 2018 Aquantia Corp. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "aqc111.h" -+ -+#define DRIVER_NAME "aqc111" -+ -+static int aqc111_read_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value, -+ u16 index, u16 size, void *data) -+{ -+ int ret; -+ -+ ret = usbnet_read_cmd_nopm(dev, cmd, USB_DIR_IN | USB_TYPE_VENDOR | -+ USB_RECIP_DEVICE, value, index, data, size); -+ -+ if (unlikely(ret < 0)) -+ netdev_warn(dev->net, -+ "Failed to read(0x%x) reg index 0x%04x: %d\n", -+ cmd, index, ret); -+ -+ return ret; -+} -+ -+static int aqc111_read_cmd(struct usbnet *dev, u8 cmd, u16 value, -+ u16 index, u16 size, void *data) -+{ -+ int ret; -+ -+ ret = usbnet_read_cmd(dev, cmd, USB_DIR_IN | USB_TYPE_VENDOR | -+ USB_RECIP_DEVICE, value, index, data, size); -+ -+ if (unlikely(ret < 0)) -+ netdev_warn(dev->net, -+ "Failed to read(0x%x) reg index 0x%04x: %d\n", -+ cmd, index, ret); -+ -+ return ret; -+} -+ -+static int aqc111_read16_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value, -+ u16 index, u16 *data) -+{ -+ int ret = 0; -+ -+ ret = aqc111_read_cmd_nopm(dev, cmd, value, index, sizeof(*data), data); -+ le16_to_cpus(data); -+ -+ return ret; -+} -+ -+static int aqc111_read16_cmd(struct usbnet *dev, u8 cmd, u16 value, -+ u16 index, u16 *data) -+{ -+ int ret = 0; -+ -+ ret = aqc111_read_cmd(dev, cmd, value, index, sizeof(*data), data); -+ le16_to_cpus(data); -+ -+ return ret; -+} -+ -+static int __aqc111_write_cmd(struct usbnet *dev, u8 cmd, u8 reqtype, -+ u16 value, u16 index, u16 size, const void *data) -+{ -+ int err = -ENOMEM; -+ void *buf = NULL; -+ -+ netdev_dbg(dev->net, -+ "%s cmd=%#x reqtype=%#x value=%#x index=%#x size=%d\n", -+ __func__, cmd, reqtype, value, index, size); -+ -+ if (data) { -+ buf = kmemdup(data, size, GFP_KERNEL); -+ if (!buf) -+ goto out; -+ } -+ -+ err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), -+ cmd, reqtype, value, index, buf, size, -+ (cmd == AQ_PHY_POWER) ? AQ_USB_PHY_SET_TIMEOUT : -+ AQ_USB_SET_TIMEOUT); -+ -+ if (unlikely(err < 0)) -+ netdev_warn(dev->net, -+ "Failed to write(0x%x) reg index 0x%04x: %d\n", -+ cmd, index, err); -+ kfree(buf); -+ -+out: -+ return err; -+} -+ -+static int aqc111_write_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value, -+ u16 index, u16 size, void *data) -+{ -+ int ret; -+ -+ ret = __aqc111_write_cmd(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR | -+ USB_RECIP_DEVICE, value, index, size, data); -+ -+ return ret; -+} -+ -+static int aqc111_write_cmd(struct usbnet *dev, u8 cmd, u16 value, -+ u16 index, u16 size, void *data) -+{ -+ int ret; -+ -+ if (usb_autopm_get_interface(dev->intf) < 0) -+ return -ENODEV; -+ -+ ret = __aqc111_write_cmd(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR | -+ USB_RECIP_DEVICE, value, index, size, data); -+ -+ usb_autopm_put_interface(dev->intf); -+ -+ return ret; -+} -+ -+static int aqc111_write16_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value, -+ u16 index, u16 *data) -+{ -+ u16 tmp = *data; -+ -+ cpu_to_le16s(&tmp); -+ -+ return aqc111_write_cmd_nopm(dev, cmd, value, index, sizeof(tmp), &tmp); -+} -+ -+static int aqc111_write16_cmd(struct usbnet *dev, u8 cmd, u16 value, -+ u16 index, u16 *data) -+{ -+ u16 tmp = *data; -+ -+ cpu_to_le16s(&tmp); -+ -+ return aqc111_write_cmd(dev, cmd, value, index, sizeof(tmp), &tmp); -+} -+ -+static int aqc111_write32_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value, -+ u16 index, u32 *data) -+{ -+ u32 tmp = *data; -+ -+ cpu_to_le32s(&tmp); -+ -+ return aqc111_write_cmd_nopm(dev, cmd, value, index, sizeof(tmp), &tmp); -+} -+ -+static int aqc111_write32_cmd(struct usbnet *dev, u8 cmd, u16 value, -+ u16 index, u32 *data) -+{ -+ u32 tmp = *data; -+ -+ cpu_to_le32s(&tmp); -+ -+ return aqc111_write_cmd(dev, cmd, value, index, sizeof(tmp), &tmp); -+} -+ -+static int aqc111_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, -+ u16 index, u16 size, void *data) -+{ -+ return usbnet_write_cmd_async(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR | -+ USB_RECIP_DEVICE, value, index, data, -+ size); -+} -+ -+static int aqc111_write16_cmd_async(struct usbnet *dev, u8 cmd, u16 value, -+ u16 index, u16 *data) -+{ -+ u16 tmp = *data; -+ -+ cpu_to_le16s(&tmp); -+ -+ return aqc111_write_cmd_async(dev, cmd, value, index, -+ sizeof(tmp), &tmp); -+} -+ -+static void aqc111_get_drvinfo(struct net_device *net, -+ struct ethtool_drvinfo *info) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ struct aqc111_data *aqc111_data = dev->driver_priv; -+ -+ /* Inherit standard device info */ -+ usbnet_get_drvinfo(net, info); -+ strlcpy(info->driver, DRIVER_NAME, sizeof(info->driver)); -+ snprintf(info->fw_version, sizeof(info->fw_version), "%u.%u.%u", -+ aqc111_data->fw_ver.major, -+ aqc111_data->fw_ver.minor, -+ aqc111_data->fw_ver.rev); -+ info->eedump_len = 0x00; -+ info->regdump_len = 0x00; -+} -+ -+static void aqc111_get_wol(struct net_device *net, -+ struct ethtool_wolinfo *wolinfo) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ struct aqc111_data *aqc111_data = dev->driver_priv; -+ -+ wolinfo->supported = WAKE_MAGIC; -+ wolinfo->wolopts = 0; -+ -+ if (aqc111_data->wol_flags & AQ_WOL_FLAG_MP) -+ wolinfo->wolopts |= WAKE_MAGIC; -+} -+ -+static int aqc111_set_wol(struct net_device *net, -+ struct ethtool_wolinfo *wolinfo) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ struct aqc111_data *aqc111_data = dev->driver_priv; -+ -+ if (wolinfo->wolopts & ~WAKE_MAGIC) -+ return -EINVAL; -+ -+ aqc111_data->wol_flags = 0; -+ if (wolinfo->wolopts & WAKE_MAGIC) -+ aqc111_data->wol_flags |= AQ_WOL_FLAG_MP; -+ -+ return 0; -+} -+ -+static void aqc111_speed_to_link_mode(u32 speed, -+ struct ethtool_link_ksettings *elk) -+{ -+ switch (speed) { -+ case SPEED_5000: -+ ethtool_link_ksettings_add_link_mode(elk, advertising, -+ 5000baseT_Full); -+ break; -+ case SPEED_2500: -+ ethtool_link_ksettings_add_link_mode(elk, advertising, -+ 2500baseT_Full); -+ break; -+ case SPEED_1000: -+ ethtool_link_ksettings_add_link_mode(elk, advertising, -+ 1000baseT_Full); -+ break; -+ case SPEED_100: -+ ethtool_link_ksettings_add_link_mode(elk, advertising, -+ 100baseT_Full); -+ break; -+ } -+} -+ -+static int aqc111_get_link_ksettings(struct net_device *net, -+ struct ethtool_link_ksettings *elk) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ struct aqc111_data *aqc111_data = dev->driver_priv; -+ enum usb_device_speed usb_speed = dev->udev->speed; -+ u32 speed = SPEED_UNKNOWN; -+ -+ ethtool_link_ksettings_zero_link_mode(elk, supported); -+ ethtool_link_ksettings_add_link_mode(elk, supported, -+ 100baseT_Full); -+ ethtool_link_ksettings_add_link_mode(elk, supported, -+ 1000baseT_Full); -+ if (usb_speed == USB_SPEED_SUPER) { -+ ethtool_link_ksettings_add_link_mode(elk, supported, -+ 2500baseT_Full); -+ ethtool_link_ksettings_add_link_mode(elk, supported, -+ 5000baseT_Full); -+ } -+ ethtool_link_ksettings_add_link_mode(elk, supported, TP); -+ ethtool_link_ksettings_add_link_mode(elk, supported, Autoneg); -+ -+ elk->base.port = PORT_TP; -+ elk->base.transceiver = XCVR_INTERNAL; -+ -+ elk->base.mdio_support = 0x00; /*Not supported*/ -+ -+ if (aqc111_data->autoneg) -+ linkmode_copy(elk->link_modes.advertising, -+ elk->link_modes.supported); -+ else -+ aqc111_speed_to_link_mode(aqc111_data->advertised_speed, elk); -+ -+ elk->base.autoneg = aqc111_data->autoneg; -+ -+ switch (aqc111_data->link_speed) { -+ case AQ_INT_SPEED_5G: -+ speed = SPEED_5000; -+ break; -+ case AQ_INT_SPEED_2_5G: -+ speed = SPEED_2500; -+ break; -+ case AQ_INT_SPEED_1G: -+ speed = SPEED_1000; -+ break; -+ case AQ_INT_SPEED_100M: -+ speed = SPEED_100; -+ break; -+ } -+ elk->base.duplex = DUPLEX_FULL; -+ elk->base.speed = speed; -+ -+ return 0; -+} -+ -+static void aqc111_set_phy_speed(struct usbnet *dev, u8 autoneg, u16 speed) -+{ -+ struct aqc111_data *aqc111_data = dev->driver_priv; -+ -+ aqc111_data->phy_cfg &= ~AQ_ADV_MASK; -+ aqc111_data->phy_cfg |= AQ_PAUSE; -+ aqc111_data->phy_cfg |= AQ_ASYM_PAUSE; -+ aqc111_data->phy_cfg |= AQ_DOWNSHIFT; -+ aqc111_data->phy_cfg &= ~AQ_DSH_RETRIES_MASK; -+ aqc111_data->phy_cfg |= (3 << AQ_DSH_RETRIES_SHIFT) & -+ AQ_DSH_RETRIES_MASK; -+ -+ if (autoneg == AUTONEG_ENABLE) { -+ switch (speed) { -+ case SPEED_5000: -+ aqc111_data->phy_cfg |= AQ_ADV_5G; -+ /* fall-through */ -+ case SPEED_2500: -+ aqc111_data->phy_cfg |= AQ_ADV_2G5; -+ /* fall-through */ -+ case SPEED_1000: -+ aqc111_data->phy_cfg |= AQ_ADV_1G; -+ /* fall-through */ -+ case SPEED_100: -+ aqc111_data->phy_cfg |= AQ_ADV_100M; -+ /* fall-through */ -+ } -+ } else { -+ switch (speed) { -+ case SPEED_5000: -+ aqc111_data->phy_cfg |= AQ_ADV_5G; -+ break; -+ case SPEED_2500: -+ aqc111_data->phy_cfg |= AQ_ADV_2G5; -+ break; -+ case SPEED_1000: -+ aqc111_data->phy_cfg |= AQ_ADV_1G; -+ break; -+ case SPEED_100: -+ aqc111_data->phy_cfg |= AQ_ADV_100M; -+ break; -+ } -+ } -+ -+ aqc111_write32_cmd(dev, AQ_PHY_OPS, 0, 0, &aqc111_data->phy_cfg); -+} -+ -+static int aqc111_set_link_ksettings(struct net_device *net, -+ const struct ethtool_link_ksettings *elk) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ struct aqc111_data *aqc111_data = dev->driver_priv; -+ enum usb_device_speed usb_speed = dev->udev->speed; -+ u8 autoneg = elk->base.autoneg; -+ u32 speed = elk->base.speed; -+ -+ if (autoneg == AUTONEG_ENABLE) { -+ if (aqc111_data->autoneg != AUTONEG_ENABLE) { -+ aqc111_data->autoneg = AUTONEG_ENABLE; -+ aqc111_data->advertised_speed = -+ (usb_speed == USB_SPEED_SUPER) ? -+ SPEED_5000 : SPEED_1000; -+ aqc111_set_phy_speed(dev, aqc111_data->autoneg, -+ aqc111_data->advertised_speed); -+ } -+ } else { -+ if (speed != SPEED_100 && -+ speed != SPEED_1000 && -+ speed != SPEED_2500 && -+ speed != SPEED_5000 && -+ speed != SPEED_UNKNOWN) -+ return -EINVAL; -+ -+ if (elk->base.duplex != DUPLEX_FULL) -+ return -EINVAL; -+ -+ if (usb_speed != USB_SPEED_SUPER && speed > SPEED_1000) -+ return -EINVAL; -+ -+ aqc111_data->autoneg = AUTONEG_DISABLE; -+ if (speed != SPEED_UNKNOWN) -+ aqc111_data->advertised_speed = speed; -+ -+ aqc111_set_phy_speed(dev, aqc111_data->autoneg, -+ aqc111_data->advertised_speed); -+ } -+ -+ return 0; -+} -+ -+static const struct ethtool_ops aqc111_ethtool_ops = { -+ .get_drvinfo = aqc111_get_drvinfo, -+ .get_wol = aqc111_get_wol, -+ .set_wol = aqc111_set_wol, -+ .get_msglevel = usbnet_get_msglevel, -+ .set_msglevel = usbnet_set_msglevel, -+ .get_link = ethtool_op_get_link, -+ .get_link_ksettings = aqc111_get_link_ksettings, -+ .set_link_ksettings = aqc111_set_link_ksettings -+}; -+ -+static int aqc111_change_mtu(struct net_device *net, int new_mtu) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ u16 reg16 = 0; -+ u8 buf[5]; -+ -+ net->mtu = new_mtu; -+ dev->hard_mtu = net->mtu + net->hard_header_len; -+ -+ aqc111_read16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE, -+ 2, ®16); -+ if (net->mtu > 1500) -+ reg16 |= SFR_MEDIUM_JUMBO_EN; -+ else -+ reg16 &= ~SFR_MEDIUM_JUMBO_EN; -+ -+ aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE, -+ 2, ®16); -+ -+ if (dev->net->mtu > 12500) { -+ memcpy(buf, &AQC111_BULKIN_SIZE[2], 5); -+ /* RX bulk configuration */ -+ aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_RX_BULKIN_QCTRL, -+ 5, 5, buf); -+ } -+ -+ /* Set high low water level */ -+ if (dev->net->mtu <= 4500) -+ reg16 = 0x0810; -+ else if (dev->net->mtu <= 9500) -+ reg16 = 0x1020; -+ else if (dev->net->mtu <= 12500) -+ reg16 = 0x1420; -+ else -+ reg16 = 0x1A20; -+ -+ aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_PAUSE_WATERLVL_LOW, -+ 2, ®16); -+ -+ return 0; -+} -+ -+static int aqc111_set_mac_addr(struct net_device *net, void *p) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ int ret = 0; -+ -+ ret = eth_mac_addr(net, p); -+ if (ret < 0) -+ return ret; -+ -+ /* Set the MAC address */ -+ return aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_NODE_ID, ETH_ALEN, -+ ETH_ALEN, net->dev_addr); -+} -+ -+static int aqc111_vlan_rx_kill_vid(struct net_device *net, -+ __be16 proto, u16 vid) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ u8 vlan_ctrl = 0; -+ u16 reg16 = 0; -+ u8 reg8 = 0; -+ -+ aqc111_read_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_CONTROL, 1, 1, ®8); -+ vlan_ctrl = reg8; -+ -+ /* Address */ -+ reg8 = (vid / 16); -+ aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_ADDRESS, 1, 1, ®8); -+ /* Data */ -+ reg8 = vlan_ctrl | SFR_VLAN_CONTROL_RD; -+ aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_CONTROL, 1, 1, ®8); -+ aqc111_read16_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_DATA0, 2, ®16); -+ reg16 &= ~(1 << (vid % 16)); -+ aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_DATA0, 2, ®16); -+ reg8 = vlan_ctrl | SFR_VLAN_CONTROL_WE; -+ aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_CONTROL, 1, 1, ®8); -+ -+ return 0; -+} -+ -+static int aqc111_vlan_rx_add_vid(struct net_device *net, __be16 proto, u16 vid) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ u8 vlan_ctrl = 0; -+ u16 reg16 = 0; -+ u8 reg8 = 0; -+ -+ aqc111_read_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_CONTROL, 1, 1, ®8); -+ vlan_ctrl = reg8; -+ -+ /* Address */ -+ reg8 = (vid / 16); -+ aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_ADDRESS, 1, 1, ®8); -+ /* Data */ -+ reg8 = vlan_ctrl | SFR_VLAN_CONTROL_RD; -+ aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_CONTROL, 1, 1, ®8); -+ aqc111_read16_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_DATA0, 2, ®16); -+ reg16 |= (1 << (vid % 16)); -+ aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_DATA0, 2, ®16); -+ reg8 = vlan_ctrl | SFR_VLAN_CONTROL_WE; -+ aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_CONTROL, 1, 1, ®8); -+ -+ return 0; -+} -+ -+static void aqc111_set_rx_mode(struct net_device *net) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ struct aqc111_data *aqc111_data = dev->driver_priv; -+ int mc_count = 0; -+ -+ mc_count = netdev_mc_count(net); -+ -+ aqc111_data->rxctl &= ~(SFR_RX_CTL_PRO | SFR_RX_CTL_AMALL | -+ SFR_RX_CTL_AM); -+ -+ if (net->flags & IFF_PROMISC) { -+ aqc111_data->rxctl |= SFR_RX_CTL_PRO; -+ } else if ((net->flags & IFF_ALLMULTI) || mc_count > AQ_MAX_MCAST) { -+ aqc111_data->rxctl |= SFR_RX_CTL_AMALL; -+ } else if (!netdev_mc_empty(net)) { -+ u8 m_filter[AQ_MCAST_FILTER_SIZE] = { 0 }; -+ struct netdev_hw_addr *ha = NULL; -+ u32 crc_bits = 0; -+ -+ netdev_for_each_mc_addr(ha, net) { -+ crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26; -+ m_filter[crc_bits >> 3] |= BIT(crc_bits & 7); -+ } -+ -+ aqc111_write_cmd_async(dev, AQ_ACCESS_MAC, -+ SFR_MULTI_FILTER_ARRY, -+ AQ_MCAST_FILTER_SIZE, -+ AQ_MCAST_FILTER_SIZE, m_filter); -+ -+ aqc111_data->rxctl |= SFR_RX_CTL_AM; -+ } -+ -+ aqc111_write16_cmd_async(dev, AQ_ACCESS_MAC, SFR_RX_CTL, -+ 2, &aqc111_data->rxctl); -+} -+ -+static int aqc111_set_features(struct net_device *net, -+ netdev_features_t features) -+{ -+ struct usbnet *dev = netdev_priv(net); -+ struct aqc111_data *aqc111_data = dev->driver_priv; -+ netdev_features_t changed = net->features ^ features; -+ u16 reg16 = 0; -+ u8 reg8 = 0; -+ -+ if (changed & NETIF_F_IP_CSUM) { -+ aqc111_read_cmd(dev, AQ_ACCESS_MAC, SFR_TXCOE_CTL, 1, 1, ®8); -+ reg8 ^= SFR_TXCOE_TCP | SFR_TXCOE_UDP; -+ aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_TXCOE_CTL, -+ 1, 1, ®8); -+ } -+ -+ if (changed & NETIF_F_IPV6_CSUM) { -+ aqc111_read_cmd(dev, AQ_ACCESS_MAC, SFR_TXCOE_CTL, 1, 1, ®8); -+ reg8 ^= SFR_TXCOE_TCPV6 | SFR_TXCOE_UDPV6; -+ aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_TXCOE_CTL, -+ 1, 1, ®8); -+ } -+ -+ if (changed & NETIF_F_RXCSUM) { -+ aqc111_read_cmd(dev, AQ_ACCESS_MAC, SFR_RXCOE_CTL, 1, 1, ®8); -+ if (features & NETIF_F_RXCSUM) { -+ aqc111_data->rx_checksum = 1; -+ reg8 &= ~(SFR_RXCOE_IP | SFR_RXCOE_TCP | SFR_RXCOE_UDP | -+ SFR_RXCOE_TCPV6 | SFR_RXCOE_UDPV6); -+ } else { -+ aqc111_data->rx_checksum = 0; -+ reg8 |= SFR_RXCOE_IP | SFR_RXCOE_TCP | SFR_RXCOE_UDP | -+ SFR_RXCOE_TCPV6 | SFR_RXCOE_UDPV6; -+ } -+ -+ aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_RXCOE_CTL, -+ 1, 1, ®8); -+ } -+ if (changed & NETIF_F_HW_VLAN_CTAG_FILTER) { -+ if (features & NETIF_F_HW_VLAN_CTAG_FILTER) { -+ u16 i = 0; -+ -+ for (i = 0; i < 256; i++) { -+ /* Address */ -+ reg8 = i; -+ aqc111_write_cmd(dev, AQ_ACCESS_MAC, -+ SFR_VLAN_ID_ADDRESS, -+ 1, 1, ®8); -+ /* Data */ -+ aqc111_write16_cmd(dev, AQ_ACCESS_MAC, -+ SFR_VLAN_ID_DATA0, -+ 2, ®16); -+ reg8 = SFR_VLAN_CONTROL_WE; -+ aqc111_write_cmd(dev, AQ_ACCESS_MAC, -+ SFR_VLAN_ID_CONTROL, -+ 1, 1, ®8); -+ } -+ aqc111_read_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_CONTROL, -+ 1, 1, ®8); -+ reg8 |= SFR_VLAN_CONTROL_VFE; -+ aqc111_write_cmd(dev, AQ_ACCESS_MAC, -+ SFR_VLAN_ID_CONTROL, 1, 1, ®8); -+ } else { -+ aqc111_read_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_CONTROL, -+ 1, 1, ®8); -+ reg8 &= ~SFR_VLAN_CONTROL_VFE; -+ aqc111_write_cmd(dev, AQ_ACCESS_MAC, -+ SFR_VLAN_ID_CONTROL, 1, 1, ®8); -+ } -+ } -+ -+ return 0; -+} -+ -+static const struct net_device_ops aqc111_netdev_ops = { -+ .ndo_open = usbnet_open, -+ .ndo_stop = usbnet_stop, -+ .ndo_start_xmit = usbnet_start_xmit, -+ .ndo_tx_timeout = usbnet_tx_timeout, -+ .ndo_get_stats64 = usbnet_get_stats64, -+ .ndo_change_mtu = aqc111_change_mtu, -+ .ndo_set_mac_address = aqc111_set_mac_addr, -+ .ndo_validate_addr = eth_validate_addr, -+ .ndo_vlan_rx_add_vid = aqc111_vlan_rx_add_vid, -+ .ndo_vlan_rx_kill_vid = aqc111_vlan_rx_kill_vid, -+ .ndo_set_rx_mode = aqc111_set_rx_mode, -+ .ndo_set_features = aqc111_set_features, -+}; -+ -+static int aqc111_read_perm_mac(struct usbnet *dev) -+{ -+ u8 buf[ETH_ALEN]; -+ int ret; -+ -+ ret = aqc111_read_cmd(dev, AQ_FLASH_PARAMETERS, 0, 0, ETH_ALEN, buf); -+ if (ret < 0) -+ goto out; -+ -+ ether_addr_copy(dev->net->perm_addr, buf); -+ -+ return 0; -+out: -+ return ret; -+} -+ -+static void aqc111_read_fw_version(struct usbnet *dev, -+ struct aqc111_data *aqc111_data) -+{ -+ aqc111_read_cmd(dev, AQ_ACCESS_MAC, AQ_FW_VER_MAJOR, -+ 1, 1, &aqc111_data->fw_ver.major); -+ aqc111_read_cmd(dev, AQ_ACCESS_MAC, AQ_FW_VER_MINOR, -+ 1, 1, &aqc111_data->fw_ver.minor); -+ aqc111_read_cmd(dev, AQ_ACCESS_MAC, AQ_FW_VER_REV, -+ 1, 1, &aqc111_data->fw_ver.rev); -+ -+ if (aqc111_data->fw_ver.major & 0x80) -+ aqc111_data->fw_ver.major &= ~0x80; -+} -+ -+static int aqc111_bind(struct usbnet *dev, struct usb_interface *intf) -+{ -+ struct usb_device *udev = interface_to_usbdev(intf); -+ enum usb_device_speed usb_speed = udev->speed; -+ struct aqc111_data *aqc111_data; -+ int ret; -+ -+ /* Check if vendor configuration */ -+ if (udev->actconfig->desc.bConfigurationValue != 1) { -+ usb_driver_set_configuration(udev, 1); -+ return -ENODEV; -+ } -+ -+ usb_reset_configuration(dev->udev); -+ -+ ret = usbnet_get_endpoints(dev, intf); -+ if (ret < 0) { -+ netdev_dbg(dev->net, "usbnet_get_endpoints failed"); -+ return ret; -+ } -+ -+ aqc111_data = kzalloc(sizeof(*aqc111_data), GFP_KERNEL); -+ if (!aqc111_data) -+ return -ENOMEM; -+ -+ /* store aqc111_data pointer in device data field */ -+ dev->driver_priv = aqc111_data; -+ -+ /* Init the MAC address */ -+ ret = aqc111_read_perm_mac(dev); -+ if (ret) -+ goto out; -+ -+ ether_addr_copy(dev->net->dev_addr, dev->net->perm_addr); -+ -+ /* Set Rx urb size */ -+ dev->rx_urb_size = URB_SIZE; -+ -+ /* Set TX needed headroom & tailroom */ -+ dev->net->needed_headroom += sizeof(u64); -+ dev->net->needed_tailroom += sizeof(u64); -+ -+ dev->net->max_mtu = 16334; -+ -+ dev->net->netdev_ops = &aqc111_netdev_ops; -+ dev->net->ethtool_ops = &aqc111_ethtool_ops; -+ -+ if (usb_device_no_sg_constraint(dev->udev)) -+ dev->can_dma_sg = 1; -+ -+ dev->net->hw_features |= AQ_SUPPORT_HW_FEATURE; -+ dev->net->features |= AQ_SUPPORT_FEATURE; -+ dev->net->vlan_features |= AQ_SUPPORT_VLAN_FEATURE; -+ -+ netif_set_gso_max_size(dev->net, 65535); -+ -+ aqc111_read_fw_version(dev, aqc111_data); -+ aqc111_data->autoneg = AUTONEG_ENABLE; -+ aqc111_data->advertised_speed = (usb_speed == USB_SPEED_SUPER) ? -+ SPEED_5000 : SPEED_1000; -+ -+ return 0; -+ -+out: -+ kfree(aqc111_data); -+ return ret; -+} -+ -+static void aqc111_unbind(struct usbnet *dev, struct usb_interface *intf) -+{ -+ struct aqc111_data *aqc111_data = dev->driver_priv; -+ u16 reg16; -+ -+ /* Force bz */ -+ reg16 = SFR_PHYPWR_RSTCTL_BZ; -+ aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_PHYPWR_RSTCTL, -+ 2, ®16); -+ reg16 = 0; -+ aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_PHYPWR_RSTCTL, -+ 2, ®16); -+ -+ /* Power down ethernet PHY */ -+ aqc111_data->phy_cfg &= ~AQ_ADV_MASK; -+ aqc111_data->phy_cfg |= AQ_LOW_POWER; -+ aqc111_data->phy_cfg &= ~AQ_PHY_POWER_EN; -+ aqc111_write32_cmd_nopm(dev, AQ_PHY_OPS, 0, 0, -+ &aqc111_data->phy_cfg); -+ -+ kfree(aqc111_data); -+} -+ -+static void aqc111_status(struct usbnet *dev, struct urb *urb) -+{ -+ struct aqc111_data *aqc111_data = dev->driver_priv; -+ u64 *event_data = NULL; -+ int link = 0; -+ -+ if (urb->actual_length < sizeof(*event_data)) -+ return; -+ -+ event_data = urb->transfer_buffer; -+ le64_to_cpus(event_data); -+ -+ if (*event_data & AQ_LS_MASK) -+ link = 1; -+ else -+ link = 0; -+ -+ aqc111_data->link_speed = (*event_data & AQ_SPEED_MASK) >> -+ AQ_SPEED_SHIFT; -+ aqc111_data->link = link; -+ -+ if (netif_carrier_ok(dev->net) != link) -+ usbnet_defer_kevent(dev, EVENT_LINK_RESET); -+} -+ -+static void aqc111_configure_rx(struct usbnet *dev, -+ struct aqc111_data *aqc111_data) -+{ -+ enum usb_device_speed usb_speed = dev->udev->speed; -+ u16 link_speed = 0, usb_host = 0; -+ u8 buf[5] = { 0 }; -+ u8 queue_num = 0; -+ u16 reg16 = 0; -+ u8 reg8 = 0; -+ -+ buf[0] = 0x00; -+ buf[1] = 0xF8; -+ buf[2] = 0x07; -+ switch (aqc111_data->link_speed) { -+ case AQ_INT_SPEED_5G: -+ link_speed = 5000; -+ reg8 = 0x05; -+ reg16 = 0x001F; -+ break; -+ case AQ_INT_SPEED_2_5G: -+ link_speed = 2500; -+ reg16 = 0x003F; -+ break; -+ case AQ_INT_SPEED_1G: -+ link_speed = 1000; -+ reg16 = 0x009F; -+ break; -+ case AQ_INT_SPEED_100M: -+ link_speed = 100; -+ queue_num = 1; -+ reg16 = 0x063F; -+ buf[1] = 0xFB; -+ buf[2] = 0x4; -+ break; -+ } -+ -+ aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_INTER_PACKET_GAP_0, -+ 1, 1, ®8); -+ -+ aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_TX_PAUSE_RESEND_T, 3, 3, buf); -+ -+ switch (usb_speed) { -+ case USB_SPEED_SUPER: -+ usb_host = 3; -+ break; -+ case USB_SPEED_HIGH: -+ usb_host = 2; -+ break; -+ case USB_SPEED_FULL: -+ case USB_SPEED_LOW: -+ usb_host = 1; -+ queue_num = 0; -+ break; -+ default: -+ usb_host = 0; -+ break; -+ } -+ -+ if (dev->net->mtu > 12500 && dev->net->mtu <= 16334) -+ queue_num = 2; /* For Jumbo packet 16KB */ -+ -+ memcpy(buf, &AQC111_BULKIN_SIZE[queue_num], 5); -+ /* RX bulk configuration */ -+ aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_RX_BULKIN_QCTRL, 5, 5, buf); -+ -+ /* Set high low water level */ -+ if (dev->net->mtu <= 4500) -+ reg16 = 0x0810; -+ else if (dev->net->mtu <= 9500) -+ reg16 = 0x1020; -+ else if (dev->net->mtu <= 12500) -+ reg16 = 0x1420; -+ else if (dev->net->mtu <= 16334) -+ reg16 = 0x1A20; -+ -+ aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_PAUSE_WATERLVL_LOW, -+ 2, ®16); -+ netdev_info(dev->net, "Link Speed %d, USB %d", link_speed, usb_host); -+} -+ -+static void aqc111_configure_csum_offload(struct usbnet *dev) -+{ -+ u8 reg8 = 0; -+ -+ if (dev->net->features & NETIF_F_RXCSUM) { -+ reg8 |= SFR_RXCOE_IP | SFR_RXCOE_TCP | SFR_RXCOE_UDP | -+ SFR_RXCOE_TCPV6 | SFR_RXCOE_UDPV6; -+ } -+ aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_RXCOE_CTL, 1, 1, ®8); -+ -+ reg8 = 0; -+ if (dev->net->features & NETIF_F_IP_CSUM) -+ reg8 |= SFR_TXCOE_IP | SFR_TXCOE_TCP | SFR_TXCOE_UDP; -+ -+ if (dev->net->features & NETIF_F_IPV6_CSUM) -+ reg8 |= SFR_TXCOE_TCPV6 | SFR_TXCOE_UDPV6; -+ -+ aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_TXCOE_CTL, 1, 1, ®8); -+} -+ -+static int aqc111_link_reset(struct usbnet *dev) -+{ -+ struct aqc111_data *aqc111_data = dev->driver_priv; -+ u16 reg16 = 0; -+ u8 reg8 = 0; -+ -+ if (aqc111_data->link == 1) { /* Link up */ -+ aqc111_configure_rx(dev, aqc111_data); -+ -+ /* Vlan Tag Filter */ -+ reg8 = SFR_VLAN_CONTROL_VSO; -+ if (dev->net->features & NETIF_F_HW_VLAN_CTAG_FILTER) -+ reg8 |= SFR_VLAN_CONTROL_VFE; -+ -+ aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_CONTROL, -+ 1, 1, ®8); -+ -+ reg8 = 0x0; -+ aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_BMRX_DMA_CONTROL, -+ 1, 1, ®8); -+ -+ aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_BMTX_DMA_CONTROL, -+ 1, 1, ®8); -+ -+ aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_ARC_CTRL, 1, 1, ®8); -+ -+ reg16 = SFR_RX_CTL_IPE | SFR_RX_CTL_AB; -+ aqc111_data->rxctl = reg16; -+ aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_RX_CTL, 2, ®16); -+ -+ reg8 = SFR_RX_PATH_READY; -+ aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_ETH_MAC_PATH, -+ 1, 1, ®8); -+ -+ reg8 = SFR_BULK_OUT_EFF_EN; -+ aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_BULK_OUT_CTRL, -+ 1, 1, ®8); -+ -+ reg16 = 0; -+ aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE, -+ 2, ®16); -+ -+ reg16 = SFR_MEDIUM_XGMIIMODE | SFR_MEDIUM_FULL_DUPLEX; -+ aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE, -+ 2, ®16); -+ -+ aqc111_configure_csum_offload(dev); -+ -+ aqc111_set_rx_mode(dev->net); -+ -+ aqc111_read16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE, -+ 2, ®16); -+ -+ if (dev->net->mtu > 1500) -+ reg16 |= SFR_MEDIUM_JUMBO_EN; -+ -+ reg16 |= SFR_MEDIUM_RECEIVE_EN | SFR_MEDIUM_RXFLOW_CTRLEN | -+ SFR_MEDIUM_TXFLOW_CTRLEN; -+ aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE, -+ 2, ®16); -+ -+ aqc111_data->rxctl |= SFR_RX_CTL_START; -+ aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_RX_CTL, -+ 2, &aqc111_data->rxctl); -+ -+ netif_carrier_on(dev->net); -+ } else { -+ aqc111_read16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE, -+ 2, ®16); -+ reg16 &= ~SFR_MEDIUM_RECEIVE_EN; -+ aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE, -+ 2, ®16); -+ -+ aqc111_data->rxctl &= ~SFR_RX_CTL_START; -+ aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_RX_CTL, -+ 2, &aqc111_data->rxctl); -+ -+ reg8 = SFR_BULK_OUT_FLUSH_EN | SFR_BULK_OUT_EFF_EN; -+ aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_BULK_OUT_CTRL, -+ 1, 1, ®8); -+ reg8 = SFR_BULK_OUT_EFF_EN; -+ aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_BULK_OUT_CTRL, -+ 1, 1, ®8); -+ -+ netif_carrier_off(dev->net); -+ } -+ return 0; -+} -+ -+static int aqc111_reset(struct usbnet *dev) -+{ -+ struct aqc111_data *aqc111_data = dev->driver_priv; -+ u8 reg8 = 0; -+ -+ dev->rx_urb_size = URB_SIZE; -+ -+ if (usb_device_no_sg_constraint(dev->udev)) -+ dev->can_dma_sg = 1; -+ -+ dev->net->hw_features |= AQ_SUPPORT_HW_FEATURE; -+ dev->net->features |= AQ_SUPPORT_FEATURE; -+ dev->net->vlan_features |= AQ_SUPPORT_VLAN_FEATURE; -+ -+ /* Power up ethernet PHY */ -+ aqc111_data->phy_cfg = AQ_PHY_POWER_EN; -+ aqc111_write32_cmd(dev, AQ_PHY_OPS, 0, 0, -+ &aqc111_data->phy_cfg); -+ -+ /* Set the MAC address */ -+ aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_NODE_ID, ETH_ALEN, -+ ETH_ALEN, dev->net->dev_addr); -+ -+ reg8 = 0xFF; -+ aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_BM_INT_MASK, 1, 1, ®8); -+ -+ reg8 = 0x0; -+ aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_SWP_CTRL, 1, 1, ®8); -+ -+ aqc111_read_cmd(dev, AQ_ACCESS_MAC, SFR_MONITOR_MODE, 1, 1, ®8); -+ reg8 &= ~(SFR_MONITOR_MODE_EPHYRW | SFR_MONITOR_MODE_RWLC | -+ SFR_MONITOR_MODE_RWMP | SFR_MONITOR_MODE_RWWF | -+ SFR_MONITOR_MODE_RW_FLAG); -+ aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_MONITOR_MODE, 1, 1, ®8); -+ -+ netif_carrier_off(dev->net); -+ -+ /* Phy advertise */ -+ aqc111_set_phy_speed(dev, aqc111_data->autoneg, -+ aqc111_data->advertised_speed); -+ -+ return 0; -+} -+ -+static int aqc111_stop(struct usbnet *dev) -+{ -+ struct aqc111_data *aqc111_data = dev->driver_priv; -+ u16 reg16 = 0; -+ -+ aqc111_read16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE, -+ 2, ®16); -+ reg16 &= ~SFR_MEDIUM_RECEIVE_EN; -+ aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE, -+ 2, ®16); -+ reg16 = 0; -+ aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_RX_CTL, 2, ®16); -+ -+ /* Put PHY to low power*/ -+ aqc111_data->phy_cfg |= AQ_LOW_POWER; -+ aqc111_write32_cmd(dev, AQ_PHY_OPS, 0, 0, -+ &aqc111_data->phy_cfg); -+ -+ netif_carrier_off(dev->net); -+ -+ return 0; -+} -+ -+static void aqc111_rx_checksum(struct sk_buff *skb, u64 pkt_desc) -+{ -+ u32 pkt_type = 0; -+ -+ skb->ip_summed = CHECKSUM_NONE; -+ /* checksum error bit is set */ -+ if (pkt_desc & AQ_RX_PD_L4_ERR || pkt_desc & AQ_RX_PD_L3_ERR) -+ return; -+ -+ pkt_type = pkt_desc & AQ_RX_PD_L4_TYPE_MASK; -+ /* It must be a TCP or UDP packet with a valid checksum */ -+ if (pkt_type == AQ_RX_PD_L4_TCP || pkt_type == AQ_RX_PD_L4_UDP) -+ skb->ip_summed = CHECKSUM_UNNECESSARY; -+} -+ -+static int aqc111_rx_fixup(struct usbnet *dev, struct sk_buff *skb) -+{ -+ struct aqc111_data *aqc111_data = dev->driver_priv; -+ struct sk_buff *new_skb = NULL; -+ u32 pkt_total_offset = 0; -+ u64 *pkt_desc_ptr = NULL; -+ u32 start_of_descs = 0; -+ u32 desc_offset = 0; /*RX Header Offset*/ -+ u16 pkt_count = 0; -+ u64 desc_hdr = 0; -+ u16 vlan_tag = 0; -+ u32 skb_len = 0; -+ -+ if (!skb) -+ goto err; -+ -+ if (skb->len == 0) -+ goto err; -+ -+ skb_len = skb->len; -+ /* RX Descriptor Header */ -+ skb_trim(skb, skb->len - sizeof(desc_hdr)); -+ desc_hdr = le64_to_cpup((u64 *)skb_tail_pointer(skb)); -+ -+ /* Check these packets */ -+ desc_offset = (desc_hdr & AQ_RX_DH_DESC_OFFSET_MASK) >> -+ AQ_RX_DH_DESC_OFFSET_SHIFT; -+ pkt_count = desc_hdr & AQ_RX_DH_PKT_CNT_MASK; -+ start_of_descs = skb_len - ((pkt_count + 1) * sizeof(desc_hdr)); -+ -+ /* self check descs position */ -+ if (start_of_descs != desc_offset) -+ goto err; -+ -+ /* self check desc_offset from header*/ -+ if (desc_offset >= skb_len) -+ goto err; -+ -+ if (pkt_count == 0) -+ goto err; -+ -+ /* Get the first RX packet descriptor */ -+ pkt_desc_ptr = (u64 *)(skb->data + desc_offset); -+ -+ while (pkt_count--) { -+ u64 pkt_desc = le64_to_cpup(pkt_desc_ptr); -+ u32 pkt_len_with_padd = 0; -+ u32 pkt_len = 0; -+ -+ pkt_len = (u32)((pkt_desc & AQ_RX_PD_LEN_MASK) >> -+ AQ_RX_PD_LEN_SHIFT); -+ pkt_len_with_padd = ((pkt_len + 7) & 0x7FFF8); -+ -+ pkt_total_offset += pkt_len_with_padd; -+ if (pkt_total_offset > desc_offset || -+ (pkt_count == 0 && pkt_total_offset != desc_offset)) { -+ goto err; -+ } -+ -+ if (pkt_desc & AQ_RX_PD_DROP || -+ !(pkt_desc & AQ_RX_PD_RX_OK) || -+ pkt_len > (dev->hard_mtu + AQ_RX_HW_PAD)) { -+ skb_pull(skb, pkt_len_with_padd); -+ /* Next RX Packet Descriptor */ -+ pkt_desc_ptr++; -+ continue; -+ } -+ -+ /* Clone SKB */ -+ new_skb = skb_clone(skb, GFP_ATOMIC); -+ -+ if (!new_skb) -+ goto err; -+ -+ new_skb->len = pkt_len; -+ skb_pull(new_skb, AQ_RX_HW_PAD); -+ skb_set_tail_pointer(new_skb, new_skb->len); -+ -+ new_skb->truesize = SKB_TRUESIZE(new_skb->len); -+ if (aqc111_data->rx_checksum) -+ aqc111_rx_checksum(new_skb, pkt_desc); -+ -+ if (pkt_desc & AQ_RX_PD_VLAN) { -+ vlan_tag = pkt_desc >> AQ_RX_PD_VLAN_SHIFT; -+ __vlan_hwaccel_put_tag(new_skb, htons(ETH_P_8021Q), -+ vlan_tag & VLAN_VID_MASK); -+ } -+ -+ usbnet_skb_return(dev, new_skb); -+ if (pkt_count == 0) -+ break; -+ -+ skb_pull(skb, pkt_len_with_padd); -+ -+ /* Next RX Packet Header */ -+ pkt_desc_ptr++; -+ -+ new_skb = NULL; -+ } -+ -+ return 1; -+ -+err: -+ return 0; -+} -+ -+static struct sk_buff *aqc111_tx_fixup(struct usbnet *dev, struct sk_buff *skb, -+ gfp_t flags) -+{ -+ int frame_size = dev->maxpacket; -+ struct sk_buff *new_skb = NULL; -+ u64 *tx_desc_ptr = NULL; -+ int padding_size = 0; -+ int headroom = 0; -+ int tailroom = 0; -+ u64 tx_desc = 0; -+ u16 tci = 0; -+ -+ /*Length of actual data*/ -+ tx_desc |= skb->len & AQ_TX_DESC_LEN_MASK; -+ -+ /* TSO MSS */ -+ tx_desc |= ((u64)(skb_shinfo(skb)->gso_size & AQ_TX_DESC_MSS_MASK)) << -+ AQ_TX_DESC_MSS_SHIFT; -+ -+ headroom = (skb->len + sizeof(tx_desc)) % 8; -+ if (headroom != 0) -+ padding_size = 8 - headroom; -+ -+ if (((skb->len + sizeof(tx_desc) + padding_size) % frame_size) == 0) { -+ padding_size += 8; -+ tx_desc |= AQ_TX_DESC_DROP_PADD; -+ } -+ -+ /* Vlan Tag */ -+ if (vlan_get_tag(skb, &tci) >= 0) { -+ tx_desc |= AQ_TX_DESC_VLAN; -+ tx_desc |= ((u64)tci & AQ_TX_DESC_VLAN_MASK) << -+ AQ_TX_DESC_VLAN_SHIFT; -+ } -+ -+ if (!dev->can_dma_sg && (dev->net->features & NETIF_F_SG) && -+ skb_linearize(skb)) -+ return NULL; -+ -+ headroom = skb_headroom(skb); -+ tailroom = skb_tailroom(skb); -+ -+ if (!(headroom >= sizeof(tx_desc) && tailroom >= padding_size)) { -+ new_skb = skb_copy_expand(skb, sizeof(tx_desc), -+ padding_size, flags); -+ dev_kfree_skb_any(skb); -+ skb = new_skb; -+ if (!skb) -+ return NULL; -+ } -+ if (padding_size != 0) -+ skb_put_zero(skb, padding_size); -+ /* Copy TX header */ -+ tx_desc_ptr = skb_push(skb, sizeof(tx_desc)); -+ *tx_desc_ptr = cpu_to_le64(tx_desc); -+ -+ usbnet_set_skb_tx_stats(skb, 1, 0); -+ -+ return skb; -+} -+ -+static const struct driver_info aqc111_info = { -+ .description = "Aquantia AQtion USB to 5GbE Controller", -+ .bind = aqc111_bind, -+ .unbind = aqc111_unbind, -+ .status = aqc111_status, -+ .link_reset = aqc111_link_reset, -+ .reset = aqc111_reset, -+ .stop = aqc111_stop, -+ .flags = FLAG_ETHER | FLAG_FRAMING_AX | -+ FLAG_AVOID_UNLINK_URBS | FLAG_MULTI_PACKET, -+ .rx_fixup = aqc111_rx_fixup, -+ .tx_fixup = aqc111_tx_fixup, -+}; -+ -+#define ASIX111_DESC \ -+"ASIX USB 3.1 Gen1 to 5G Multi-Gigabit Ethernet Adapter" -+ -+static const struct driver_info asix111_info = { -+ .description = ASIX111_DESC, -+ .bind = aqc111_bind, -+ .unbind = aqc111_unbind, -+ .status = aqc111_status, -+ .link_reset = aqc111_link_reset, -+ .reset = aqc111_reset, -+ .stop = aqc111_stop, -+ .flags = FLAG_ETHER | FLAG_FRAMING_AX | -+ FLAG_AVOID_UNLINK_URBS | FLAG_MULTI_PACKET, -+ .rx_fixup = aqc111_rx_fixup, -+ .tx_fixup = aqc111_tx_fixup, -+}; -+ -+#undef ASIX111_DESC -+ -+#define ASIX112_DESC \ -+"ASIX USB 3.1 Gen1 to 2.5G Multi-Gigabit Ethernet Adapter" -+ -+static const struct driver_info asix112_info = { -+ .description = ASIX112_DESC, -+ .bind = aqc111_bind, -+ .unbind = aqc111_unbind, -+ .status = aqc111_status, -+ .link_reset = aqc111_link_reset, -+ .reset = aqc111_reset, -+ .stop = aqc111_stop, -+ .flags = FLAG_ETHER | FLAG_FRAMING_AX | -+ FLAG_AVOID_UNLINK_URBS | FLAG_MULTI_PACKET, -+ .rx_fixup = aqc111_rx_fixup, -+ .tx_fixup = aqc111_tx_fixup, -+}; -+ -+#undef ASIX112_DESC -+ -+static const struct driver_info trendnet_info = { -+ .description = "USB-C 3.1 to 5GBASE-T Ethernet Adapter", -+ .bind = aqc111_bind, -+ .unbind = aqc111_unbind, -+ .status = aqc111_status, -+ .link_reset = aqc111_link_reset, -+ .reset = aqc111_reset, -+ .stop = aqc111_stop, -+ .flags = FLAG_ETHER | FLAG_FRAMING_AX | -+ FLAG_AVOID_UNLINK_URBS | FLAG_MULTI_PACKET, -+ .rx_fixup = aqc111_rx_fixup, -+ .tx_fixup = aqc111_tx_fixup, -+}; -+ -+static const struct driver_info qnap_info = { -+ .description = "QNAP QNA-UC5G1T USB to 5GbE Adapter", -+ .bind = aqc111_bind, -+ .unbind = aqc111_unbind, -+ .status = aqc111_status, -+ .link_reset = aqc111_link_reset, -+ .reset = aqc111_reset, -+ .stop = aqc111_stop, -+ .flags = FLAG_ETHER | FLAG_FRAMING_AX | -+ FLAG_AVOID_UNLINK_URBS | FLAG_MULTI_PACKET, -+ .rx_fixup = aqc111_rx_fixup, -+ .tx_fixup = aqc111_tx_fixup, -+}; -+ -+static int aqc111_suspend(struct usb_interface *intf, pm_message_t message) -+{ -+ struct usbnet *dev = usb_get_intfdata(intf); -+ struct aqc111_data *aqc111_data = dev->driver_priv; -+ u16 temp_rx_ctrl = 0x00; -+ u16 reg16; -+ u8 reg8; -+ -+ usbnet_suspend(intf, message); -+ -+ aqc111_read16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_RX_CTL, 2, ®16); -+ temp_rx_ctrl = reg16; -+ /* Stop RX operations*/ -+ reg16 &= ~SFR_RX_CTL_START; -+ aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_RX_CTL, 2, ®16); -+ /* Force bz */ -+ aqc111_read16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_PHYPWR_RSTCTL, -+ 2, ®16); -+ reg16 |= SFR_PHYPWR_RSTCTL_BZ; -+ aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_PHYPWR_RSTCTL, -+ 2, ®16); -+ -+ reg8 = SFR_BULK_OUT_EFF_EN; -+ aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_BULK_OUT_CTRL, -+ 1, 1, ®8); -+ -+ temp_rx_ctrl &= ~(SFR_RX_CTL_START | SFR_RX_CTL_RF_WAK | -+ SFR_RX_CTL_AP | SFR_RX_CTL_AM); -+ aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_RX_CTL, -+ 2, &temp_rx_ctrl); -+ -+ reg8 = 0x00; -+ aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_ETH_MAC_PATH, -+ 1, 1, ®8); -+ -+ if (aqc111_data->wol_flags) { -+ struct aqc111_wol_cfg wol_cfg; -+ -+ memset(&wol_cfg, 0, sizeof(struct aqc111_wol_cfg)); -+ -+ aqc111_data->phy_cfg |= AQ_WOL; -+ ether_addr_copy(wol_cfg.hw_addr, dev->net->dev_addr); -+ wol_cfg.flags = aqc111_data->wol_flags; -+ -+ temp_rx_ctrl |= (SFR_RX_CTL_AB | SFR_RX_CTL_START); -+ aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_RX_CTL, -+ 2, &temp_rx_ctrl); -+ reg8 = 0x00; -+ aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_BM_INT_MASK, -+ 1, 1, ®8); -+ reg8 = SFR_BMRX_DMA_EN; -+ aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_BMRX_DMA_CONTROL, -+ 1, 1, ®8); -+ reg8 = SFR_RX_PATH_READY; -+ aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_ETH_MAC_PATH, -+ 1, 1, ®8); -+ reg8 = 0x07; -+ aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_RX_BULKIN_QCTRL, -+ 1, 1, ®8); -+ reg8 = 0x00; -+ aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, -+ SFR_RX_BULKIN_QTIMR_LOW, 1, 1, ®8); -+ aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, -+ SFR_RX_BULKIN_QTIMR_HIGH, 1, 1, ®8); -+ reg8 = 0xFF; -+ aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_RX_BULKIN_QSIZE, -+ 1, 1, ®8); -+ aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_RX_BULKIN_QIFG, -+ 1, 1, ®8); -+ -+ aqc111_read16_cmd_nopm(dev, AQ_ACCESS_MAC, -+ SFR_MEDIUM_STATUS_MODE, 2, ®16); -+ reg16 |= SFR_MEDIUM_RECEIVE_EN; -+ aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, -+ SFR_MEDIUM_STATUS_MODE, 2, ®16); -+ -+ aqc111_write_cmd(dev, AQ_WOL_CFG, 0, 0, -+ WOL_CFG_SIZE, &wol_cfg); -+ aqc111_write32_cmd(dev, AQ_PHY_OPS, 0, 0, -+ &aqc111_data->phy_cfg); -+ } else { -+ aqc111_data->phy_cfg |= AQ_LOW_POWER; -+ aqc111_write32_cmd(dev, AQ_PHY_OPS, 0, 0, -+ &aqc111_data->phy_cfg); -+ -+ /* Disable RX path */ -+ aqc111_read16_cmd_nopm(dev, AQ_ACCESS_MAC, -+ SFR_MEDIUM_STATUS_MODE, 2, ®16); -+ reg16 &= ~SFR_MEDIUM_RECEIVE_EN; -+ aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, -+ SFR_MEDIUM_STATUS_MODE, 2, ®16); -+ } -+ -+ return 0; -+} -+ -+static int aqc111_resume(struct usb_interface *intf) -+{ -+ struct usbnet *dev = usb_get_intfdata(intf); -+ struct aqc111_data *aqc111_data = dev->driver_priv; -+ u16 reg16; -+ u8 reg8; -+ -+ netif_carrier_off(dev->net); -+ -+ /* Power up ethernet PHY */ -+ aqc111_data->phy_cfg |= AQ_PHY_POWER_EN; -+ aqc111_data->phy_cfg &= ~AQ_LOW_POWER; -+ aqc111_data->phy_cfg &= ~AQ_WOL; -+ -+ reg8 = 0xFF; -+ aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_BM_INT_MASK, -+ 1, 1, ®8); -+ /* Configure RX control register => start operation */ -+ reg16 = aqc111_data->rxctl; -+ reg16 &= ~SFR_RX_CTL_START; -+ aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_RX_CTL, 2, ®16); -+ -+ reg16 |= SFR_RX_CTL_START; -+ aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_RX_CTL, 2, ®16); -+ -+ aqc111_set_phy_speed(dev, aqc111_data->autoneg, -+ aqc111_data->advertised_speed); -+ -+ aqc111_read16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE, -+ 2, ®16); -+ reg16 |= SFR_MEDIUM_RECEIVE_EN; -+ aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE, -+ 2, ®16); -+ reg8 = SFR_RX_PATH_READY; -+ aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_ETH_MAC_PATH, -+ 1, 1, ®8); -+ reg8 = 0x0; -+ aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_BMRX_DMA_CONTROL, 1, 1, ®8); -+ -+ return usbnet_resume(intf); -+} -+ -+#define AQC111_USB_ETH_DEV(vid, pid, table) \ -+ USB_DEVICE_INTERFACE_CLASS((vid), (pid), USB_CLASS_VENDOR_SPEC), \ -+ .driver_info = (unsigned long)&(table) \ -+}, \ -+{ \ -+ USB_DEVICE_AND_INTERFACE_INFO((vid), (pid), \ -+ USB_CLASS_COMM, \ -+ USB_CDC_SUBCLASS_ETHERNET, \ -+ USB_CDC_PROTO_NONE), \ -+ .driver_info = (unsigned long)&(table), -+ -+static const struct usb_device_id products[] = { -+ {AQC111_USB_ETH_DEV(0x2eca, 0xc101, aqc111_info)}, -+ {AQC111_USB_ETH_DEV(0x0b95, 0x2790, asix111_info)}, -+ {AQC111_USB_ETH_DEV(0x0b95, 0x2791, asix112_info)}, -+ {AQC111_USB_ETH_DEV(0x20f4, 0xe05a, trendnet_info)}, -+ {AQC111_USB_ETH_DEV(0x1c04, 0x0015, qnap_info)}, -+ { },/* END */ -+}; -+MODULE_DEVICE_TABLE(usb, products); -+ -+static struct usb_driver aq_driver = { -+ .name = "aqc111", -+ .id_table = products, -+ .probe = usbnet_probe, -+ .suspend = aqc111_suspend, -+ .resume = aqc111_resume, -+ .disconnect = usbnet_disconnect, -+}; -+ -+module_usb_driver(aq_driver); -+ -+MODULE_DESCRIPTION("Aquantia AQtion USB to 5/2.5GbE Controllers"); -+MODULE_LICENSE("GPL"); ---- /dev/null -+++ b/drivers/net/usb/aqc111.h -@@ -0,0 +1,232 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+/* Aquantia Corp. Aquantia AQtion USB to 5GbE Controller -+ * Copyright (C) 2003-2005 David Hollis -+ * Copyright (C) 2005 Phil Chang -+ * Copyright (C) 2002-2003 TiVo Inc. -+ * Copyright (C) 2017-2018 ASIX -+ * Copyright (C) 2018 Aquantia Corp. -+ */ -+ -+#ifndef __LINUX_USBNET_AQC111_H -+#define __LINUX_USBNET_AQC111_H -+ -+#define URB_SIZE (1024 * 62) -+ -+#define AQ_MCAST_FILTER_SIZE 8 -+#define AQ_MAX_MCAST 64 -+ -+#define AQ_ACCESS_MAC 0x01 -+#define AQ_FLASH_PARAMETERS 0x20 -+#define AQ_PHY_POWER 0x31 -+#define AQ_WOL_CFG 0x60 -+#define AQ_PHY_OPS 0x61 -+ -+#define AQ_USB_PHY_SET_TIMEOUT 10000 -+#define AQ_USB_SET_TIMEOUT 4000 -+ -+/* Feature. ********************************************/ -+#define AQ_SUPPORT_FEATURE (NETIF_F_SG | NETIF_F_IP_CSUM |\ -+ NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM |\ -+ NETIF_F_TSO | NETIF_F_HW_VLAN_CTAG_TX |\ -+ NETIF_F_HW_VLAN_CTAG_RX) -+ -+#define AQ_SUPPORT_HW_FEATURE (NETIF_F_SG | NETIF_F_IP_CSUM |\ -+ NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM |\ -+ NETIF_F_TSO | NETIF_F_HW_VLAN_CTAG_FILTER) -+ -+#define AQ_SUPPORT_VLAN_FEATURE (NETIF_F_SG | NETIF_F_IP_CSUM |\ -+ NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM |\ -+ NETIF_F_TSO) -+ -+/* SFR Reg. ********************************************/ -+ -+#define SFR_GENERAL_STATUS 0x03 -+#define SFR_CHIP_STATUS 0x05 -+#define SFR_RX_CTL 0x0B -+ #define SFR_RX_CTL_TXPADCRC 0x0400 -+ #define SFR_RX_CTL_IPE 0x0200 -+ #define SFR_RX_CTL_DROPCRCERR 0x0100 -+ #define SFR_RX_CTL_START 0x0080 -+ #define SFR_RX_CTL_RF_WAK 0x0040 -+ #define SFR_RX_CTL_AP 0x0020 -+ #define SFR_RX_CTL_AM 0x0010 -+ #define SFR_RX_CTL_AB 0x0008 -+ #define SFR_RX_CTL_AMALL 0x0002 -+ #define SFR_RX_CTL_PRO 0x0001 -+ #define SFR_RX_CTL_STOP 0x0000 -+#define SFR_INTER_PACKET_GAP_0 0x0D -+#define SFR_NODE_ID 0x10 -+#define SFR_MULTI_FILTER_ARRY 0x16 -+#define SFR_MEDIUM_STATUS_MODE 0x22 -+ #define SFR_MEDIUM_XGMIIMODE 0x0001 -+ #define SFR_MEDIUM_FULL_DUPLEX 0x0002 -+ #define SFR_MEDIUM_RXFLOW_CTRLEN 0x0010 -+ #define SFR_MEDIUM_TXFLOW_CTRLEN 0x0020 -+ #define SFR_MEDIUM_JUMBO_EN 0x0040 -+ #define SFR_MEDIUM_RECEIVE_EN 0x0100 -+#define SFR_MONITOR_MODE 0x24 -+ #define SFR_MONITOR_MODE_EPHYRW 0x01 -+ #define SFR_MONITOR_MODE_RWLC 0x02 -+ #define SFR_MONITOR_MODE_RWMP 0x04 -+ #define SFR_MONITOR_MODE_RWWF 0x08 -+ #define SFR_MONITOR_MODE_RW_FLAG 0x10 -+ #define SFR_MONITOR_MODE_PMEPOL 0x20 -+ #define SFR_MONITOR_MODE_PMETYPE 0x40 -+#define SFR_PHYPWR_RSTCTL 0x26 -+ #define SFR_PHYPWR_RSTCTL_BZ 0x0010 -+ #define SFR_PHYPWR_RSTCTL_IPRL 0x0020 -+#define SFR_VLAN_ID_ADDRESS 0x2A -+#define SFR_VLAN_ID_CONTROL 0x2B -+ #define SFR_VLAN_CONTROL_WE 0x0001 -+ #define SFR_VLAN_CONTROL_RD 0x0002 -+ #define SFR_VLAN_CONTROL_VSO 0x0010 -+ #define SFR_VLAN_CONTROL_VFE 0x0020 -+#define SFR_VLAN_ID_DATA0 0x2C -+#define SFR_VLAN_ID_DATA1 0x2D -+#define SFR_RX_BULKIN_QCTRL 0x2E -+ #define SFR_RX_BULKIN_QCTRL_TIME 0x01 -+ #define SFR_RX_BULKIN_QCTRL_IFG 0x02 -+ #define SFR_RX_BULKIN_QCTRL_SIZE 0x04 -+#define SFR_RX_BULKIN_QTIMR_LOW 0x2F -+#define SFR_RX_BULKIN_QTIMR_HIGH 0x30 -+#define SFR_RX_BULKIN_QSIZE 0x31 -+#define SFR_RX_BULKIN_QIFG 0x32 -+#define SFR_RXCOE_CTL 0x34 -+ #define SFR_RXCOE_IP 0x01 -+ #define SFR_RXCOE_TCP 0x02 -+ #define SFR_RXCOE_UDP 0x04 -+ #define SFR_RXCOE_ICMP 0x08 -+ #define SFR_RXCOE_IGMP 0x10 -+ #define SFR_RXCOE_TCPV6 0x20 -+ #define SFR_RXCOE_UDPV6 0x40 -+ #define SFR_RXCOE_ICMV6 0x80 -+#define SFR_TXCOE_CTL 0x35 -+ #define SFR_TXCOE_IP 0x01 -+ #define SFR_TXCOE_TCP 0x02 -+ #define SFR_TXCOE_UDP 0x04 -+ #define SFR_TXCOE_ICMP 0x08 -+ #define SFR_TXCOE_IGMP 0x10 -+ #define SFR_TXCOE_TCPV6 0x20 -+ #define SFR_TXCOE_UDPV6 0x40 -+ #define SFR_TXCOE_ICMV6 0x80 -+#define SFR_BM_INT_MASK 0x41 -+#define SFR_BMRX_DMA_CONTROL 0x43 -+ #define SFR_BMRX_DMA_EN 0x80 -+#define SFR_BMTX_DMA_CONTROL 0x46 -+#define SFR_PAUSE_WATERLVL_LOW 0x54 -+#define SFR_PAUSE_WATERLVL_HIGH 0x55 -+#define SFR_ARC_CTRL 0x9E -+#define SFR_SWP_CTRL 0xB1 -+#define SFR_TX_PAUSE_RESEND_T 0xB2 -+#define SFR_ETH_MAC_PATH 0xB7 -+ #define SFR_RX_PATH_READY 0x01 -+#define SFR_BULK_OUT_CTRL 0xB9 -+ #define SFR_BULK_OUT_FLUSH_EN 0x01 -+ #define SFR_BULK_OUT_EFF_EN 0x02 -+ -+#define AQ_FW_VER_MAJOR 0xDA -+#define AQ_FW_VER_MINOR 0xDB -+#define AQ_FW_VER_REV 0xDC -+ -+/*PHY_OPS**********************************************************************/ -+ -+#define AQ_ADV_100M BIT(0) -+#define AQ_ADV_1G BIT(1) -+#define AQ_ADV_2G5 BIT(2) -+#define AQ_ADV_5G BIT(3) -+#define AQ_ADV_MASK 0x0F -+ -+#define AQ_PAUSE BIT(16) -+#define AQ_ASYM_PAUSE BIT(17) -+#define AQ_LOW_POWER BIT(18) -+#define AQ_PHY_POWER_EN BIT(19) -+#define AQ_WOL BIT(20) -+#define AQ_DOWNSHIFT BIT(21) -+ -+#define AQ_DSH_RETRIES_SHIFT 0x18 -+#define AQ_DSH_RETRIES_MASK 0xF000000 -+ -+#define AQ_WOL_FLAG_MP 0x2 -+ -+/******************************************************************************/ -+ -+struct aqc111_wol_cfg { -+ u8 hw_addr[6]; -+ u8 flags; -+ u8 rsvd[283]; -+} __packed; -+ -+#define WOL_CFG_SIZE sizeof(struct aqc111_wol_cfg) -+ -+struct aqc111_data { -+ u16 rxctl; -+ u8 rx_checksum; -+ u8 link_speed; -+ u8 link; -+ u8 autoneg; -+ u32 advertised_speed; -+ struct { -+ u8 major; -+ u8 minor; -+ u8 rev; -+ } fw_ver; -+ u32 phy_cfg; -+ u8 wol_flags; -+}; -+ -+#define AQ_LS_MASK 0x8000 -+#define AQ_SPEED_MASK 0x7F00 -+#define AQ_SPEED_SHIFT 0x0008 -+#define AQ_INT_SPEED_5G 0x000F -+#define AQ_INT_SPEED_2_5G 0x0010 -+#define AQ_INT_SPEED_1G 0x0011 -+#define AQ_INT_SPEED_100M 0x0013 -+ -+/* TX Descriptor */ -+#define AQ_TX_DESC_LEN_MASK 0x1FFFFF -+#define AQ_TX_DESC_DROP_PADD BIT(28) -+#define AQ_TX_DESC_VLAN BIT(29) -+#define AQ_TX_DESC_MSS_MASK 0x7FFF -+#define AQ_TX_DESC_MSS_SHIFT 0x20 -+#define AQ_TX_DESC_VLAN_MASK 0xFFFF -+#define AQ_TX_DESC_VLAN_SHIFT 0x30 -+ -+#define AQ_RX_HW_PAD 0x02 -+ -+/* RX Packet Descriptor */ -+#define AQ_RX_PD_L4_ERR BIT(0) -+#define AQ_RX_PD_L3_ERR BIT(1) -+#define AQ_RX_PD_L4_TYPE_MASK 0x1C -+#define AQ_RX_PD_L4_UDP 0x04 -+#define AQ_RX_PD_L4_TCP 0x10 -+#define AQ_RX_PD_L3_TYPE_MASK 0x60 -+#define AQ_RX_PD_L3_IP 0x20 -+#define AQ_RX_PD_L3_IP6 0x40 -+ -+#define AQ_RX_PD_VLAN BIT(10) -+#define AQ_RX_PD_RX_OK BIT(11) -+#define AQ_RX_PD_DROP BIT(31) -+#define AQ_RX_PD_LEN_MASK 0x7FFF0000 -+#define AQ_RX_PD_LEN_SHIFT 0x10 -+#define AQ_RX_PD_VLAN_SHIFT 0x20 -+ -+/* RX Descriptor header */ -+#define AQ_RX_DH_PKT_CNT_MASK 0x1FFF -+#define AQ_RX_DH_DESC_OFFSET_MASK 0xFFFFE000 -+#define AQ_RX_DH_DESC_OFFSET_SHIFT 0x0D -+ -+static struct { -+ unsigned char ctrl; -+ unsigned char timer_l; -+ unsigned char timer_h; -+ unsigned char size; -+ unsigned char ifg; -+} AQC111_BULKIN_SIZE[] = { -+ /* xHCI & EHCI & OHCI */ -+ {7, 0x00, 0x01, 0x1E, 0xFF},/* 10G, 5G, 2.5G, 1G */ -+ {7, 0xA0, 0x00, 0x14, 0x00},/* 100M */ -+ /* Jumbo packet */ -+ {7, 0x00, 0x01, 0x18, 0xFF}, -+}; -+ -+#endif /* __LINUX_USBNET_AQC111_H */ ---- /dev/null -+++ b/include/linux/linkmode.h -@@ -0,0 +1,91 @@ -+#ifndef __LINKMODE_H -+#define __LINKMODE_H -+ -+#include -+#include -+#include -+ -+static inline void linkmode_zero(unsigned long *dst) -+{ -+ bitmap_zero(dst, __ETHTOOL_LINK_MODE_MASK_NBITS); -+} -+ -+static inline void linkmode_copy(unsigned long *dst, const unsigned long *src) -+{ -+ bitmap_copy(dst, src, __ETHTOOL_LINK_MODE_MASK_NBITS); -+} -+ -+static inline void linkmode_and(unsigned long *dst, const unsigned long *a, -+ const unsigned long *b) -+{ -+ bitmap_and(dst, a, b, __ETHTOOL_LINK_MODE_MASK_NBITS); -+} -+ -+static inline void linkmode_or(unsigned long *dst, const unsigned long *a, -+ const unsigned long *b) -+{ -+ bitmap_or(dst, a, b, __ETHTOOL_LINK_MODE_MASK_NBITS); -+} -+ -+static inline bool linkmode_empty(const unsigned long *src) -+{ -+ return bitmap_empty(src, __ETHTOOL_LINK_MODE_MASK_NBITS); -+} -+ -+static inline int linkmode_andnot(unsigned long *dst, const unsigned long *src1, -+ const unsigned long *src2) -+{ -+ return bitmap_andnot(dst, src1, src2, __ETHTOOL_LINK_MODE_MASK_NBITS); -+} -+ -+static inline void linkmode_set_bit(int nr, volatile unsigned long *addr) -+{ -+ __set_bit(nr, addr); -+} -+ -+static inline void linkmode_set_bit_array(const int *array, int array_size, -+ unsigned long *addr) -+{ -+ int i; -+ -+ for (i = 0; i < array_size; i++) -+ linkmode_set_bit(array[i], addr); -+} -+ -+static inline void linkmode_clear_bit(int nr, volatile unsigned long *addr) -+{ -+ __clear_bit(nr, addr); -+} -+ -+static inline void linkmode_mod_bit(int nr, volatile unsigned long *addr, -+ int set) -+{ -+ if (set) -+ linkmode_set_bit(nr, addr); -+ else -+ linkmode_clear_bit(nr, addr); -+} -+ -+static inline void linkmode_change_bit(int nr, volatile unsigned long *addr) -+{ -+ __change_bit(nr, addr); -+} -+ -+static inline int linkmode_test_bit(int nr, volatile unsigned long *addr) -+{ -+ return test_bit(nr, addr); -+} -+ -+static inline int linkmode_equal(const unsigned long *src1, -+ const unsigned long *src2) -+{ -+ return bitmap_equal(src1, src2, __ETHTOOL_LINK_MODE_MASK_NBITS); -+} -+ -+static inline int linkmode_subset(const unsigned long *src1, -+ const unsigned long *src2) -+{ -+ return bitmap_subset(src1, src2, __ETHTOOL_LINK_MODE_MASK_NBITS); -+} -+ -+#endif /* __LINKMODE_H */ ---- a/drivers/net/usb/cdc_ether.c -+++ b/drivers/net/usb/cdc_ether.c -@@ -569,6 +569,8 @@ static const struct driver_info wwan_inf - #define MICROSOFT_VENDOR_ID 0x045e - #define UBLOX_VENDOR_ID 0x1546 - #define TPLINK_VENDOR_ID 0x2357 -+#define AQUANTIA_VENDOR_ID 0x2eca -+#define ASIX_VENDOR_ID 0x0b95 - - static const struct usb_device_id products[] = { - /* BLACKLIST !! -@@ -835,6 +837,30 @@ static const struct usb_device_id produc - .driver_info = 0, - }, - -+/* Aquantia AQtion USB to 5GbE Controller (based on AQC111U) */ -+{ -+ USB_DEVICE_AND_INTERFACE_INFO(AQUANTIA_VENDOR_ID, 0xc101, -+ USB_CLASS_COMM, USB_CDC_SUBCLASS_ETHERNET, -+ USB_CDC_PROTO_NONE), -+ .driver_info = 0, -+}, -+ -+/* ASIX USB 3.1 Gen1 to 5G Multi-Gigabit Ethernet Adapter(based on AQC111U) */ -+{ -+ USB_DEVICE_AND_INTERFACE_INFO(ASIX_VENDOR_ID, 0x2790, USB_CLASS_COMM, -+ USB_CDC_SUBCLASS_ETHERNET, -+ USB_CDC_PROTO_NONE), -+ .driver_info = 0, -+}, -+ -+/* ASIX USB 3.1 Gen1 to 2.5G Multi-Gigabit Ethernet Adapter(based on AQC112U) */ -+{ -+ USB_DEVICE_AND_INTERFACE_INFO(ASIX_VENDOR_ID, 0x2791, USB_CLASS_COMM, -+ USB_CDC_SUBCLASS_ETHERNET, -+ USB_CDC_PROTO_NONE), -+ .driver_info = 0, -+}, -+ - /* WHITELIST!!! - * - * CDC Ether uses two interfaces, not necessarily consecutive. diff --git a/target/linux/generic/pending-5.4/770-02-net-ethernet-mtk_eth_soc-fix-rx-vlan-offload.patch b/target/linux/generic/pending-5.4/770-02-net-ethernet-mtk_eth_soc-fix-rx-vlan-offload.patch index 899bc41c93..e17877028f 100644 --- a/target/linux/generic/pending-5.4/770-02-net-ethernet-mtk_eth_soc-fix-rx-vlan-offload.patch +++ b/target/linux/generic/pending-5.4/770-02-net-ethernet-mtk_eth_soc-fix-rx-vlan-offload.patch @@ -21,7 +21,7 @@ Signed-off-by: Felix Fietkau skb_record_rx_queue(skb, 0); --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -293,6 +293,7 @@ +@@ -295,6 +295,7 @@ #define RX_DMA_LSO BIT(30) #define RX_DMA_PLEN0(_x) (((_x) & 0x3fff) << 16) #define RX_DMA_GET_PLEN0(_x) (((_x) >> 16) & 0x3fff) diff --git a/target/linux/generic/pending-5.4/770-04-net-ethernet-mtk_eth_soc-use-larger-burst-size-for-q.patch b/target/linux/generic/pending-5.4/770-04-net-ethernet-mtk_eth_soc-use-larger-burst-size-for-q.patch index b066d35d2e..e135f27f95 100644 --- a/target/linux/generic/pending-5.4/770-04-net-ethernet-mtk_eth_soc-use-larger-burst-size-for-q.patch +++ b/target/linux/generic/pending-5.4/770-04-net-ethernet-mtk_eth_soc-use-larger-burst-size-for-q.patch @@ -21,7 +21,7 @@ Signed-off-by: Felix Fietkau MTK_QDMA_GLO_CFG); --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -195,7 +195,7 @@ +@@ -197,7 +197,7 @@ #define MTK_RX_BT_32DWORDS (3 << 11) #define MTK_NDP_CO_PRO BIT(10) #define MTK_TX_WB_DDONE BIT(6) diff --git a/target/linux/generic/pending-5.4/770-06-net-ethernet-mtk_eth_soc-implement-dynamic-interrupt.patch b/target/linux/generic/pending-5.4/770-06-net-ethernet-mtk_eth_soc-implement-dynamic-interrupt.patch index 4372029423..8720ab503c 100644 --- a/target/linux/generic/pending-5.4/770-06-net-ethernet-mtk_eth_soc-implement-dynamic-interrupt.patch +++ b/target/linux/generic/pending-5.4/770-06-net-ethernet-mtk_eth_soc-implement-dynamic-interrupt.patch @@ -95,7 +95,7 @@ Signed-off-by: Felix Fietkau if (likely(napi_schedule_prep(ð->tx_napi))) { __napi_schedule(ð->tx_napi); mtk_tx_irq_disable(eth, MTK_TX_DONE_INT); -@@ -2282,6 +2299,9 @@ static int mtk_stop(struct net_device *d +@@ -2311,6 +2328,9 @@ static int mtk_stop(struct net_device *d napi_disable(ð->tx_napi); napi_disable(ð->rx_napi); @@ -105,7 +105,7 @@ Signed-off-by: Felix Fietkau if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) mtk_stop_dma(eth, MTK_QDMA_GLO_CFG); mtk_stop_dma(eth, MTK_PDMA_GLO_CFG); -@@ -2331,6 +2351,64 @@ err_disable_clks: +@@ -2360,6 +2380,64 @@ err_disable_clks: return ret; } @@ -170,7 +170,7 @@ Signed-off-by: Felix Fietkau static int mtk_hw_init(struct mtk_eth *eth) { int i, val, ret; -@@ -2352,9 +2430,6 @@ static int mtk_hw_init(struct mtk_eth *e +@@ -2381,9 +2459,6 @@ static int mtk_hw_init(struct mtk_eth *e goto err_disable_pm; } @@ -180,7 +180,7 @@ Signed-off-by: Felix Fietkau /* disable delay and normal interrupt */ mtk_tx_irq_disable(eth, ~0); mtk_rx_irq_disable(eth, ~0); -@@ -2393,11 +2468,10 @@ static int mtk_hw_init(struct mtk_eth *e +@@ -2422,11 +2497,10 @@ static int mtk_hw_init(struct mtk_eth *e /* Enable RX VLan Offloading */ mtk_w32(eth, 1, MTK_CDMP_EG_CTRL); @@ -193,8 +193,8 @@ Signed-off-by: Felix Fietkau - mtk_w32(eth, 0, MTK_QDMA_DELAY_INT); mtk_tx_irq_disable(eth, ~0); mtk_rx_irq_disable(eth, ~0); - mtk_w32(eth, RST_GL_PSE, MTK_RST_GL); -@@ -2916,6 +2990,13 @@ static int mtk_probe(struct platform_dev + +@@ -2930,6 +3004,13 @@ static int mtk_probe(struct platform_dev spin_lock_init(ð->page_lock); spin_lock_init(ð->tx_irq_lock); spin_lock_init(ð->rx_irq_lock); @@ -218,7 +218,7 @@ Signed-off-by: Felix Fietkau #define MTK_QDMA_PAGE_SIZE 2048 #define MTK_MAX_RX_LENGTH 1536 -@@ -129,13 +130,18 @@ +@@ -131,13 +132,18 @@ /* PDMA Delay Interrupt Register */ #define MTK_PDMA_DELAY_INT 0xa0c @@ -242,7 +242,7 @@ Signed-off-by: Felix Fietkau /* PDMA Interrupt Status Register */ #define MTK_PDMA_INT_STATUS 0xa20 -@@ -217,6 +223,7 @@ +@@ -219,6 +225,7 @@ /* QDMA Interrupt Status Register */ #define MTK_QDMA_INT_STATUS 0x1A18 #define MTK_RX_DONE_DLY BIT(30) @@ -250,7 +250,7 @@ Signed-off-by: Felix Fietkau #define MTK_RX_DONE_INT3 BIT(19) #define MTK_RX_DONE_INT2 BIT(18) #define MTK_RX_DONE_INT1 BIT(17) -@@ -226,8 +233,7 @@ +@@ -228,8 +235,7 @@ #define MTK_TX_DONE_INT1 BIT(1) #define MTK_TX_DONE_INT0 BIT(0) #define MTK_RX_DONE_INT MTK_RX_DONE_DLY @@ -260,7 +260,7 @@ Signed-off-by: Felix Fietkau /* QDMA Interrupt grouping registers */ #define MTK_QDMA_INT_GRP1 0x1a20 -@@ -890,6 +896,18 @@ struct mtk_eth { +@@ -892,6 +898,18 @@ struct mtk_eth { const struct mtk_soc_data *soc; diff --git a/target/linux/generic/pending-5.4/770-08-net-ethernet-mtk_eth_soc-cache-hardware-pointer-of-l.patch b/target/linux/generic/pending-5.4/770-08-net-ethernet-mtk_eth_soc-cache-hardware-pointer-of-l.patch index 29dfd932e7..a7231d826c 100644 --- a/target/linux/generic/pending-5.4/770-08-net-ethernet-mtk_eth_soc-cache-hardware-pointer-of-l.patch +++ b/target/linux/generic/pending-5.4/770-08-net-ethernet-mtk_eth_soc-cache-hardware-pointer-of-l.patch @@ -49,7 +49,7 @@ Signed-off-by: Felix Fietkau } else { --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -622,6 +622,7 @@ struct mtk_tx_buf { +@@ -624,6 +624,7 @@ struct mtk_tx_buf { * @phys: The physical addr of tx_buf * @next_free: Pointer to the next free descriptor * @last_free: Pointer to the last free descriptor @@ -57,7 +57,7 @@ Signed-off-by: Felix Fietkau * @thresh: The threshold of minimum amount of free descriptors * @free_count: QDMA uses a linked list. Track how many free descriptors * are present -@@ -632,6 +633,7 @@ struct mtk_tx_ring { +@@ -634,6 +635,7 @@ struct mtk_tx_ring { dma_addr_t phys; struct mtk_tx_dma *next_free; struct mtk_tx_dma *last_free; diff --git a/target/linux/generic/pending-5.4/770-13-net-ethernet-mtk_eth_soc-fix-parsing-packets-in-GDM.patch b/target/linux/generic/pending-5.4/770-13-net-ethernet-mtk_eth_soc-fix-parsing-packets-in-GDM.patch new file mode 100644 index 0000000000..36a52bbc06 --- /dev/null +++ b/target/linux/generic/pending-5.4/770-13-net-ethernet-mtk_eth_soc-fix-parsing-packets-in-GDM.patch @@ -0,0 +1,75 @@ +From: Felix Fietkau +Date: Sun, 13 Sep 2020 08:17:02 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: fix parsing packets in GDM + +When using DSA, set the special tag in GDM ingress control to allow the MAC +to parse packets properly earlier. This affects rx DMA source port reporting. + +Signed-off-by: Felix Fietkau +--- + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -19,6 +19,7 @@ + #include + #include + #include ++#include + + #include "mtk_eth_soc.h" + +@@ -1240,12 +1241,14 @@ static int mtk_poll_rx(struct napi_struc + u8 *data, *new_data; + struct mtk_rx_dma *rxd, trxd; + int done = 0, bytes = 0; ++ bool uses_dsa = eth->netdev[0] && netdev_uses_dsa(eth->netdev[0]); + + while (done < budget) { + struct net_device *netdev; + unsigned int pktlen; + dma_addr_t dma_addr; + int mac; ++ u16 hash; + + ring = mtk_get_rx_ring(eth); + if (unlikely(!ring)) +@@ -1259,13 +1262,13 @@ static int mtk_poll_rx(struct napi_struc + break; + + /* find out which mac the packet come from. values start at 1 */ +- if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) { ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) + mac = 0; +- } else { +- mac = (trxd.rxd4 >> RX_DMA_FPORT_SHIFT) & +- RX_DMA_FPORT_MASK; +- mac--; +- } ++ else if (uses_dsa) ++ mac = !(trxd.rxd4 >> 22); ++ else ++ mac = ((trxd.rxd4 >> RX_DMA_FPORT_SHIFT) & ++ RX_DMA_FPORT_MASK) - 1; + + if (unlikely(mac < 0 || mac >= MTK_MAC_COUNT || + !eth->netdev[mac])) +@@ -2247,6 +2250,9 @@ static void mtk_gdm_config(struct mtk_et + + val |= config; + ++ if (!i && eth->netdev[0] && netdev_uses_dsa(eth->netdev[0])) ++ val |= MTK_GDMA_SPECIAL_TAG; ++ + mtk_w32(eth, val, MTK_GDMA_FWD_CFG(i)); + } + /* Reset and enable PSE */ +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -82,6 +82,7 @@ + + /* GDM Exgress Control Register */ + #define MTK_GDMA_FWD_CFG(x) (0x500 + (x * 0x1000)) ++#define MTK_GDMA_SPECIAL_TAG BIT(24) + #define MTK_GDMA_ICS_EN BIT(22) + #define MTK_GDMA_TCS_EN BIT(21) + #define MTK_GDMA_UCS_EN BIT(20) diff --git a/target/linux/generic/pending-5.4/770-14-net-ethernet-mtk_eth_soc-set-PPE-flow-hash-as-skb-ha.patch b/target/linux/generic/pending-5.4/770-14-net-ethernet-mtk_eth_soc-set-PPE-flow-hash-as-skb-ha.patch new file mode 100644 index 0000000000..384af9d21d --- /dev/null +++ b/target/linux/generic/pending-5.4/770-14-net-ethernet-mtk_eth_soc-set-PPE-flow-hash-as-skb-ha.patch @@ -0,0 +1,23 @@ +From: Felix Fietkau +Date: Sun, 13 Sep 2020 08:27:24 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: set PPE flow hash as skb hash + if present + +This improves GRO performance + +Signed-off-by: Felix Fietkau +--- + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -1318,6 +1318,10 @@ static int mtk_poll_rx(struct napi_struc + skb->protocol = eth_type_trans(skb, netdev); + bytes += pktlen; + ++ hash = trxd.rxd4 & GENMASK(13, 0); ++ if (hash != GENMASK(13, 0)) ++ skb_set_hash(skb, hash, PKT_HASH_TYPE_L4); ++ + if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX && + (trxd.rxd2 & RX_DMA_VTAG)) + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), diff --git a/target/linux/mediatek/patches-5.4/0999-hnat.patch b/target/linux/mediatek/patches-5.4/0999-hnat.patch index 50850a906e..685c741043 100644 --- a/target/linux/mediatek/patches-5.4/0999-hnat.patch +++ b/target/linux/mediatek/patches-5.4/0999-hnat.patch @@ -144,10 +144,10 @@ #include +#include +#include + #include #include "mtk_eth_soc.h" - -@@ -1320,8 +1322,16 @@ static int mtk_poll_rx(struct napi_struc +@@ -1327,8 +1329,16 @@ static int mtk_poll_rx(struct napi_struc (trxd.rxd2 & RX_DMA_VTAG)) __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), RX_DMA_VID(trxd.rxd3)); @@ -166,7 +166,7 @@ skip_rx: ring->data[idx] = new_data; -@@ -2255,6 +2265,9 @@ static int mtk_open(struct net_device *d +@@ -2292,6 +2302,9 @@ static int mtk_open(struct net_device *d mtk_tx_irq_enable(eth, MTK_TX_DONE_INT); mtk_rx_irq_enable(eth, MTK_RX_DONE_INT); refcount_set(ð->dma_refcnt, 1); @@ -176,7 +176,7 @@ } else refcount_inc(ð->dma_refcnt); -@@ -2316,6 +2329,9 @@ static int mtk_stop(struct net_device *d +@@ -2355,6 +2368,9 @@ static int mtk_stop(struct net_device *d mtk_dma_free(eth); @@ -186,7 +186,7 @@ return 0; } -@@ -2829,6 +2845,27 @@ static int mtk_set_rxnfc(struct net_devi +@@ -2853,6 +2869,27 @@ static int mtk_set_rxnfc(struct net_devi return ret; } @@ -214,7 +214,7 @@ static const struct ethtool_ops mtk_ethtool_ops = { .get_link_ksettings = mtk_get_link_ksettings, .set_link_ksettings = mtk_set_link_ksettings, -@@ -2860,6 +2897,9 @@ static const struct net_device_ops mtk_n +@@ -2884,6 +2921,9 @@ static const struct net_device_ops mtk_n #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = mtk_poll_controller, #endif @@ -224,7 +224,7 @@ }; static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np) -@@ -3202,6 +3242,7 @@ static const struct mtk_soc_data mt7622_ +@@ -3226,6 +3266,7 @@ static const struct mtk_soc_data mt7622_ .hw_features = MTK_HW_FEATURES, .required_clks = MT7622_CLKS_BITMAP, .required_pctl = false, @@ -234,7 +234,7 @@ static const struct mtk_soc_data mt7623_data = { --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -787,6 +787,13 @@ enum mkt_eth_capabilities { +@@ -790,6 +790,13 @@ enum mkt_eth_capabilities { MTK_MUX_U3_GMAC2_TO_QPHY | \ MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA) @@ -248,7 +248,7 @@ /* struct mtk_eth_data - This is the structure holding all differences * among various plaforms * @ana_rgc3: The offset for register ANA_RGC3 related to -@@ -804,6 +811,7 @@ struct mtk_soc_data { +@@ -807,6 +814,7 @@ struct mtk_soc_data { u32 required_clks; bool required_pctl; netdev_features_t hw_features; @@ -256,7 +256,7 @@ }; /* currently no SoC has more than 2 macs */ -@@ -829,6 +837,23 @@ struct mtk_sgmii { +@@ -832,6 +840,23 @@ struct mtk_sgmii { u32 ana_rgc3; }; @@ -280,7 +280,7 @@ /* struct mtk_eth - This is the main datasructure for holding the state * of the driver * @dev: The device pointer -@@ -914,6 +939,16 @@ struct mtk_eth { +@@ -917,6 +942,16 @@ struct mtk_eth { u32 tx_int_status_reg; u32 rx_dma_l4_valid; int ip_align; @@ -297,7 +297,7 @@ }; /* struct mtk_mac - the structure that holds the info about the MACs of the -@@ -946,6 +981,7 @@ void mtk_stats_update_mac(struct mtk_mac +@@ -949,6 +984,7 @@ void mtk_stats_update_mac(struct mtk_mac void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg); u32 mtk_r32(struct mtk_eth *eth, unsigned reg); @@ -305,7 +305,7 @@ int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *np, u32 ana_rgc3); -@@ -958,4 +994,13 @@ int mtk_gmac_sgmii_path_setup(struct mtk +@@ -961,4 +997,13 @@ int mtk_gmac_sgmii_path_setup(struct mtk int mtk_gmac_gephy_path_setup(struct mtk_eth *eth, int mac_id); int mtk_gmac_rgmii_path_setup(struct mtk_eth *eth, int mac_id); diff --git a/target/linux/mediatek/patches-5.4/1011-net-ethernet-mtk_eth_soc-add-support-for-coherent-DM.patch b/target/linux/mediatek/patches-5.4/1011-net-ethernet-mtk_eth_soc-add-support-for-coherent-DM.patch index cc104927d8..ed9aecdf74 100644 --- a/target/linux/mediatek/patches-5.4/1011-net-ethernet-mtk_eth_soc-add-support-for-coherent-DM.patch +++ b/target/linux/mediatek/patches-5.4/1011-net-ethernet-mtk_eth_soc-add-support-for-coherent-DM.patch @@ -37,7 +37,7 @@ Signed-off-by: Felix Fietkau #include #include #include -@@ -2476,6 +2477,12 @@ static int mtk_hw_init(struct mtk_eth *e +@@ -2486,6 +2487,12 @@ static int mtk_hw_init(struct mtk_eth *e if (ret) goto err_disable_pm; @@ -50,7 +50,7 @@ Signed-off-by: Felix Fietkau if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) { ret = device_reset(eth->dev); if (ret) { -@@ -3078,6 +3085,16 @@ static int mtk_probe(struct platform_dev +@@ -3088,6 +3095,16 @@ static int mtk_probe(struct platform_dev } } @@ -69,7 +69,7 @@ Signed-off-by: Felix Fietkau GFP_KERNEL); --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -425,6 +425,11 @@ +@@ -426,6 +426,11 @@ #define RSTCTRL_FE BIT(6) #define RSTCTRL_PPE BIT(31) diff --git a/target/linux/ramips/dts/mt7621_d-team_pbr-m1.dts b/target/linux/ramips/dts/mt7621_d-team_pbr-m1.dts index 6b4d3b35d4..beaa380818 100644 --- a/target/linux/ramips/dts/mt7621_d-team_pbr-m1.dts +++ b/target/linux/ramips/dts/mt7621_d-team_pbr-m1.dts @@ -109,7 +109,8 @@ flash@0 { compatible = "jedec,spi-nor"; reg = <0>; - spi-max-frequency = <10000000>; + spi-max-frequency = <50000000>; + m25p,fast-read; broken-flash-reset; partitions { @@ -138,14 +139,26 @@ partition@50000 { compatible = "denx,uimage"; label = "firmware"; - reg = <0x50000 0xfb0000>; + reg = <0x50000 0x1fb0000>; }; }; }; }; +&pinctrl { + uart3_gpio: uart3-gpio { + uart3 { + groups = "uart3"; + function = "gpio"; + }; + }; +}; + &pcie { status = "okay"; + pinctrl-0 = <&pcie_pins>, <&uart3_gpio>; + reset-gpios = <&gpio 19 GPIO_ACTIVE_LOW>, + <&gpio 7 GPIO_ACTIVE_LOW>; }; &pcie0 { diff --git a/target/linux/ramips/image/mt7621.mk b/target/linux/ramips/image/mt7621.mk index c8d286a8e2..c603b27dcb 100644 --- a/target/linux/ramips/image/mt7621.mk +++ b/target/linux/ramips/image/mt7621.mk @@ -333,7 +333,7 @@ TARGET_DEVICES += d-team_newifi-d2 define Device/d-team_pbr-m1 $(Device/dsa-migration) - IMAGE_SIZE := 16064k + IMAGE_SIZE := 32448k DEVICE_VENDOR := PandoraBox DEVICE_MODEL := PBR-M1 DEVICE_PACKAGES := kmod-ata-ahci kmod-mt7603 kmod-mt76x2 kmod-sdhci-mt7620 \ diff --git a/target/linux/rtl838x/Makefile b/target/linux/rtl838x/Makefile new file mode 100644 index 0000000000..764b94ae38 --- /dev/null +++ b/target/linux/rtl838x/Makefile @@ -0,0 +1,22 @@ +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# +include $(TOPDIR)/rules.mk + +ARCH:=mips +CPU_TYPE:=4kec +BOARD:=rtl838x +BOARDNAME:=Realtek MIPS +FEATURES:=ramdisk squashfs + +KERNEL_PATCHVER:=5.4 + +define Target/Description + Build firmware images for Realtek RTL838x based boards. +endef + +include $(INCLUDE_DIR)/target.mk + +DEFAULT_PACKAGES += swconfig uboot-envtools ethtool kmod-gpio-button-hotplug + +$(eval $(call BuildTarget)) diff --git a/target/linux/rtl838x/base-files/etc/board.d/01_leds b/target/linux/rtl838x/base-files/etc/board.d/01_leds new file mode 100755 index 0000000000..7991b832f6 --- /dev/null +++ b/target/linux/rtl838x/base-files/etc/board.d/01_leds @@ -0,0 +1,15 @@ +#!/bin/sh + +. /lib/functions/uci-defaults.sh + +board=$(board_name) +boardname="${board##*,}" + +board_config_update + +case $board in +esac + +board_config_flush + +exit 0 diff --git a/target/linux/rtl838x/base-files/etc/board.d/02_network b/target/linux/rtl838x/base-files/etc/board.d/02_network new file mode 100755 index 0000000000..a79869d8c3 --- /dev/null +++ b/target/linux/rtl838x/base-files/etc/board.d/02_network @@ -0,0 +1,87 @@ +#!/bin/sh + +. /lib/functions.sh +. /lib/functions/uci-defaults.sh +. /lib/functions/system.sh + +rtl838x_setup_switch() +{ + local switchid net portid master device lan_role lan_list + + json_select_object switch + # Find slave ports + for net in $(ls -d /sys/class/net/*); do + switchid=$(cat $net/phys_switch_id 2>/dev/null) + [ -z "$switchid" ] && continue + device=$(basename $net) + portid=$(cat $net/phys_port_name) + lan_role="$lan_role ${portid##p}" + lan_list="$lan_list $device" + json_select_object "switch$((switchid))" + json_add_boolean enable 1 + json_add_boolean reset 0 + json_add_boolean dsa 1 + json_select_array ports + json_add_object + json_add_int num "${portid##p}" + json_add_string role "lan" + json_add_string device "$device" + json_close_object + json_select .. + json_select .. + done + # Add switch master device + for net in $(ls -d /sys/class/net/*/dsa); do + master=$(dirname $net) + device=$(basename $master) + portid=$(cat $master/phys_port_name) + lan_role="$lan_role ${portid##p}" + json_select_object "switch$((switchid))" + json_select_array ports + json_add_object + json_add_int num "${portid##p}" + json_add_string device "$device" + json_add_boolean need_tag 0 + json_add_boolean want_untag 0 + json_add_boolean master 1 + json_close_object + json_select .. + json_select_array roles + json_add_object + json_add_string role "lan" + lan_role=$(echo $lan_role | xargs -n1 | sort -n | xargs) + json_add_string ports "$lan_role" + json_close_object + json_select .. + json_select .. + done + json_select .. + lan_list=$(echo $lan_list | xargs -n1 | sort -V | xargs) + ucidef_set_interface_lan "$lan_list" +} + +rtl838x_setup_macs() +{ + local board="$1" + local lan_mac + local wan_mac + local label_mac + + case $board in + allnet,all-sg8208m) + lan_mac=$(mtd_get_mac_ascii u-boot-env ethaddr) + label_mac=$lan_mac + esac + + [ -n "$lan_mac" ] && ucidef_set_interface_macaddr "lan" $lan_mac + [ -n "$wan_mac" ] && ucidef_set_interface_macaddr "wan" $wan_mac + [ -n "$label_mac" ] && ucidef_set_label_macaddr $label_mac +} + +board_config_update +board=$(board_name) +rtl838x_setup_switch +rtl838x_setup_macs $board +board_config_flush + +exit 0 diff --git a/target/linux/rtl838x/base-files/etc/inittab b/target/linux/rtl838x/base-files/etc/inittab new file mode 100644 index 0000000000..9820e7144b --- /dev/null +++ b/target/linux/rtl838x/base-files/etc/inittab @@ -0,0 +1,3 @@ +::sysinit:/etc/init.d/rcS S boot +::shutdown:/etc/init.d/rcS K shutdown +::askconsole:/usr/libexec/login.sh diff --git a/target/linux/rtl838x/base-files/lib/firmware/rtl838x_phy/rtl838x_8214fc.fw b/target/linux/rtl838x/base-files/lib/firmware/rtl838x_phy/rtl838x_8214fc.fw new file mode 100644 index 0000000000..035c02d804 Binary files /dev/null and b/target/linux/rtl838x/base-files/lib/firmware/rtl838x_phy/rtl838x_8214fc.fw differ diff --git a/target/linux/rtl838x/base-files/lib/firmware/rtl838x_phy/rtl838x_8218b.fw b/target/linux/rtl838x/base-files/lib/firmware/rtl838x_phy/rtl838x_8218b.fw new file mode 100644 index 0000000000..a907849fb9 Binary files /dev/null and b/target/linux/rtl838x/base-files/lib/firmware/rtl838x_phy/rtl838x_8218b.fw differ diff --git a/target/linux/rtl838x/base-files/lib/firmware/rtl838x_phy/rtl838x_8380.fw b/target/linux/rtl838x/base-files/lib/firmware/rtl838x_phy/rtl838x_8380.fw new file mode 100644 index 0000000000..ef84c71753 Binary files /dev/null and b/target/linux/rtl838x/base-files/lib/firmware/rtl838x_phy/rtl838x_8380.fw differ diff --git a/target/linux/rtl838x/base-files/lib/upgrade/platform.sh b/target/linux/rtl838x/base-files/lib/upgrade/platform.sh new file mode 100644 index 0000000000..927aadbe31 --- /dev/null +++ b/target/linux/rtl838x/base-files/lib/upgrade/platform.sh @@ -0,0 +1,19 @@ +PART_NAME=firmware +REQUIRE_IMAGE_METADATA=1 + +RAMFS_COPY_BIN='fw_printenv fw_setenv' +RAMFS_COPY_DATA='/etc/fw_env.config /var/lock/fw_printenv.lock' + +platform_check_image() { + return 0 +} + +platform_do_upgrade() { + local board=$(board_name) + + case "$board" in + *) + default_do_upgrade "$1" + ;; + esac +} diff --git a/target/linux/rtl838x/config-5.4 b/target/linux/rtl838x/config-5.4 new file mode 100644 index 0000000000..4cd2fddba3 --- /dev/null +++ b/target/linux/rtl838x/config-5.4 @@ -0,0 +1,226 @@ +CONFIG_ARCH_32BIT_OFF_T=y +CONFIG_ARCH_CLOCKSOURCE_DATA=y +CONFIG_ARCH_HAS_DMA_COHERENT_TO_PFN=y +CONFIG_ARCH_HAS_DMA_PREP_COHERENT=y +CONFIG_ARCH_HAS_DMA_WRITE_COMBINE=y +CONFIG_ARCH_HAS_ELF_RANDOMIZE=y +CONFIG_ARCH_HAS_RESET_CONTROLLER=y +CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE=y +CONFIG_ARCH_HAS_UNCACHED_SEGMENT=y +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_MMAP_RND_BITS_MAX=15 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=15 +CONFIG_ARCH_SUPPORTS_UPROBES=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARCH_USE_BUILTIN_BSWAP=y +CONFIG_ARCH_USE_MEMREMAP_PROT=y +CONFIG_ARCH_USE_QUEUED_RWLOCKS=y +CONFIG_ARCH_USE_QUEUED_SPINLOCKS=y +CONFIG_ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT=y +CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_CEVT_R4K=y +CONFIG_CLKDEV_LOOKUP=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_COMMON_CLK=y +# CONFIG_COMMON_CLK_BOSTON is not set +CONFIG_COMPAT_32BIT_TIME=y +CONFIG_CONSOLE_LOGLEVEL_DEFAULT=15 +CONFIG_CPU_BIG_ENDIAN=y +CONFIG_CPU_GENERIC_DUMP_TLB=y +CONFIG_CPU_HAS_LOAD_STORE_LR=y +CONFIG_CPU_HAS_PREFETCH=y +CONFIG_CPU_HAS_RIXI=y +CONFIG_CPU_HAS_SYNC=y +CONFIG_CPU_MIPS32=y +# CONFIG_CPU_MIPS32_R1 is not set +CONFIG_CPU_MIPS32_R2=y +CONFIG_CPU_MIPSR2=y +CONFIG_CPU_NEEDS_NO_SMARTMIPS_OR_MICROMIPS=y +CONFIG_CPU_R4K_CACHE_TLB=y +CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y +CONFIG_CPU_SUPPORTS_HIGHMEM=y +CONFIG_CPU_SUPPORTS_MSA=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CSRC_R4K=y +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_SECTION_MISMATCH=y +CONFIG_DMA_NONCOHERENT=y +CONFIG_DMA_NONCOHERENT_CACHE_SYNC=y +CONFIG_DTC=y +CONFIG_EARLY_PRINTK=y +CONFIG_EFI_EARLYCON=y +CONFIG_ETHERNET_PACKET_MANGLE=y +# CONFIG_FIXED_PHY is not set +# CONFIG_FONT_SUPPORT is not set +CONFIG_FW_LOADER_PAGED_BUF=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CMOS_UPDATE=y +CONFIG_GENERIC_CPU_AUTOPROBE=y +CONFIG_GENERIC_GETTIMEOFDAY=y +CONFIG_GENERIC_IOMAP=y +CONFIG_GENERIC_IRQ_CHIP=y +CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y +CONFIG_GENERIC_IRQ_IPI=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_LIB_ASHLDI3=y +CONFIG_GENERIC_LIB_ASHRDI3=y +CONFIG_GENERIC_LIB_CMPDI2=y +CONFIG_GENERIC_LIB_LSHRDI3=y +CONFIG_GENERIC_LIB_UCMPDI2=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_PHY=y +CONFIG_GENERIC_SCHED_CLOCK=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_GENERIC_TIME_VSYSCALL=y +CONFIG_GPIOLIB=y +CONFIG_GPIO_SYSFS=y +CONFIG_HANDLE_DOMAIN_IRQ=y +CONFIG_HARDWARE_WATCHPOINTS=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_HAVE_ARCH_COMPILER_H=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y +CONFIG_HAVE_ARCH_KGDB=y +CONFIG_HAVE_ARCH_SECCOMP_FILTER=y +CONFIG_HAVE_ARCH_TRACEHOOK=y +CONFIG_HAVE_ASM_MODVERSIONS=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_CONTEXT_TRACKING=y +CONFIG_HAVE_COPY_THREAD_TLS=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_HAVE_DEBUG_KMEMLEAK=y +CONFIG_HAVE_DEBUG_STACKOVERFLOW=y +CONFIG_HAVE_DMA_CONTIGUOUS=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FAST_GUP=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_GENERIC_VDSO=y +CONFIG_HAVE_IDE=y +CONFIG_HAVE_IOREMAP_PROT=y +CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK=y +CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y +CONFIG_HAVE_KVM=y +CONFIG_HAVE_LD_DEAD_CODE_DATA_ELIMINATION=y +CONFIG_HAVE_MEMBLOCK_NODE_MAP=y +CONFIG_HAVE_MOD_ARCH_SPECIFIC=y +CONFIG_HAVE_NET_DSA=y +CONFIG_HAVE_OPROFILE=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_RSEQ=y +CONFIG_HAVE_SYSCALL_TRACEPOINTS=y +CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y +# CONFIG_HIGH_RES_TIMERS is not set +CONFIG_HZ_PERIODIC=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_IRQCHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_DOMAIN_HIERARCHY=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_MIPS_CPU=y +CONFIG_IRQ_WORK=y +CONFIG_LEDS_GPIO=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +CONFIG_LIBFDT=y +CONFIG_LOCK_DEBUGGING_SUPPORT=y +CONFIG_MDIO_BUS=y +CONFIG_MDIO_DEVICE=y +CONFIG_MEMFD_CREATE=y +CONFIG_MFD_SYSCON=y +CONFIG_MIGRATION=y +CONFIG_MIPS=y +CONFIG_MIPS_ASID_BITS=8 +CONFIG_MIPS_ASID_SHIFT=0 +CONFIG_MIPS_CLOCK_VSYSCALL=y +# CONFIG_MIPS_CMDLINE_DTB_EXTEND is not set +# CONFIG_MIPS_CMDLINE_FROM_BOOTLOADER is not set +CONFIG_MIPS_CMDLINE_FROM_DTB=y +# CONFIG_MIPS_ELF_APPENDED_DTB is not set +CONFIG_MIPS_L1_CACHE_SHIFT=5 +# CONFIG_MIPS_MT_SMP is not set +# CONFIG_MIPS_NO_APPENDED_DTB is not set +CONFIG_MIPS_RAW_APPENDED_DTB=y +CONFIG_MIPS_SPRAM=y +# CONFIG_MIPS_VPE_LOADER is not set +CONFIG_MODULES_USE_ELF_REL=y +CONFIG_MTD_CFI_ADV_OPTIONS=y +CONFIG_MTD_CFI_GEOMETRY=y +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_JEDECPROBE=y +CONFIG_MTD_SPI_NOR=y +CONFIG_MTD_SPLIT_BRNIMAGE_FW=y +CONFIG_MTD_SPLIT_EVA_FW=y +CONFIG_MTD_SPLIT_FIRMWARE=y +CONFIG_MTD_SPLIT_TPLINK_FW=y +CONFIG_MTD_SPLIT_UIMAGE_FW=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NEED_PER_CPU_KM=y +CONFIG_NET_DEVLINK=y +CONFIG_NET_DSA=y +CONFIG_NET_DSA_RTL838X=y +CONFIG_NET_DSA_TAG_TRAILER=y +CONFIG_NET_RTL838X=y +CONFIG_NET_SWITCHDEV=y +CONFIG_NO_GENERIC_PCI_IOPORT_MAP=y +CONFIG_NVMEM=y +CONFIG_OF=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_FLATTREE=y +CONFIG_OF_GPIO=y +CONFIG_OF_IRQ=y +CONFIG_OF_KOBJ=y +CONFIG_OF_MDIO=y +CONFIG_OF_NET=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_PCI_DRIVERS_LEGACY=y +CONFIG_PERF_USE_VMALLOC=y +CONFIG_PGTABLE_LEVELS=2 +# CONFIG_PHYLIB is not set +CONFIG_PHYLINK=y +CONFIG_PINCTRL=y +# CONFIG_PINCTRL_SINGLE is not set +CONFIG_POWER_RESET=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_PSB6970_PHY=y +CONFIG_REGMAP=y +CONFIG_REGMAP_MMIO=y +CONFIG_RESET_CONTROLLER=y +CONFIG_RTL838X=y +CONFIG_GPIO_RTL838X=y +CONFIG_SERIAL_8250_RUNTIME_UARTS=2 +CONFIG_SERIAL_8250_NR_UARTS=2 +CONFIG_SERIAL_MCTRL_GPIO=y +CONFIG_SPI=y +CONFIG_SPI_MASTER=y +CONFIG_SPI_MEM=y +CONFIG_SPI_RTL838X=y +CONFIG_SRCU=y +CONFIG_SWAP_IO_SPACE=y +CONFIG_SWCONFIG=y +CONFIG_SWPHY=y +CONFIG_SYSCTL_EXCEPTION_TRACE=y +CONFIG_SYS_HAS_CPU_MIPS32_R1=y +CONFIG_SYS_HAS_CPU_MIPS32_R2=y +CONFIG_SYS_HAS_EARLY_PRINTK=y +CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y +CONFIG_SYS_SUPPORTS_ARBIT_HZ=y +CONFIG_SYS_SUPPORTS_BIG_ENDIAN=y +CONFIG_SYS_SUPPORTS_MIPS16=y +CONFIG_SYS_SUPPORTS_MULTITHREADING=y +CONFIG_SYS_SUPPORTS_VPE_LOADER=y +CONFIG_TARGET_ISA_REV=2 +CONFIG_TICK_CPU_ACCOUNTING=y +CONFIG_TINY_SRCU=y +CONFIG_USE_OF=y +CONFIG_JFFS2_ZLIB=y diff --git a/target/linux/rtl838x/dts/rtl8382_allnet_all-sg8208m.dts b/target/linux/rtl838x/dts/rtl8382_allnet_all-sg8208m.dts new file mode 100644 index 0000000000..09d5aff0fe --- /dev/null +++ b/target/linux/rtl838x/dts/rtl8382_allnet_all-sg8208m.dts @@ -0,0 +1,221 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/dts-v1/; + +#include "rtl838x.dtsi" + +#include +#include + +/ { + compatible = "allnet,all-sg8208m", "realtek,rtl838x-soc"; + model = "ALLNET ALL-SG8208M"; + + aliases { + led-boot = &led_sys; + led-failsafe = &led_sys; + led-running = &led_sys; + led-upgrade = &led_sys; + }; + + chosen { + bootargs = "console=ttyS0,115200"; + }; + + keys { + compatible = "gpio-keys-polled"; + poll-interval = <20>; + + reset { + label = "reset"; + gpios = <&gpio0 67 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + leds { + compatible = "gpio-leds"; + + led_sys: sys { + label = "all-sg8208m:green:sys"; + gpios = <&gpio0 47 GPIO_ACTIVE_HIGH>; + }; + // GPIO 25: power on/off all port leds + }; +}; + +&gpio0 { + indirect-access-bus-id = <0>; +}; + +&spi0 { + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <10000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "u-boot"; + reg = <0x0 0x80000>; + read-only; + }; + + partition@80000 { + label = "u-boot-env"; + reg = <0x80000 0x10000>; + read-only; + }; + + partition@90000 { + label = "u-boot-env2"; + reg = <0x90000 0x10000>; + read-only; + }; + + partition@a0000 { + label = "jffs"; + reg = <0xa0000 0x100000>; + }; + + partition@1a0000 { + label = "jffs2"; + reg = <0x1a0000 0x100000>; + }; + + partition@2a0000 { + label = "firmware"; + reg = <0x2a0000 0xd60000>; + compatible = "allnet,uimage"; + }; + }; + }; +}; + +ðernet0 { + mdio: mdio-bus { + compatible = "realtek,rtl838x-mdio"; + regmap = <ðernet0>; + #address-cells = <1>; + #size-cells = <0>; + + /* Internal phy */ + phy8: ethernet-phy@8 { + reg = <8>; + compatible = "ethernet-phy-ieee802.3-c22"; + }; + + phy9: ethernet-phy@9 { + reg = <9>; + compatible = "ethernet-phy-ieee802.3-c22"; + }; + + phy10: ethernet-phy@10 { + reg = <10>; + compatible = "ethernet-phy-ieee802.3-c22"; + }; + + phy11: ethernet-phy@11 { + reg = <11>; + compatible = "ethernet-phy-ieee802.3-c22"; + }; + + phy12: ethernet-phy@12 { + reg = <12>; + compatible = "ethernet-phy-ieee802.3-c22"; + }; + + phy13: ethernet-phy@13 { + reg = <13>; + compatible = "ethernet-phy-ieee802.3-c22"; + }; + + phy14: ethernet-phy@14 { + reg = <14>; + compatible = "ethernet-phy-ieee802.3-c22"; + }; + + phy15: ethernet-phy@15 { + reg = <15>; + compatible = "ethernet-phy-ieee802.3-c22"; + }; + }; +}; + +&switch0 { + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <8>; + label = "lan1"; + phy-handle = <&phy8>; + phy-mode = "internal"; + }; + + port@1 { + reg = <9>; + label = "lan2"; + phy-handle = <&phy9>; + phy-mode = "internal"; + }; + + port@2 { + reg = <10>; + label = "lan3"; + phy-handle = <&phy10>; + phy-mode = "internal"; + }; + + port@3 { + reg = <11>; + label = "lan4"; + phy-handle = <&phy11>; + phy-mode = "internal"; + }; + + port@4 { + reg = <12>; + label = "lan5"; + phy-handle = <&phy12>; + phy-mode = "internal"; + }; + + port@5 { + reg = <13>; + label = "lan6"; + phy-handle = <&phy13>; + phy-mode = "internal"; + }; + + port@6 { + reg = <14>; + label = "lan7"; + phy-handle = <&phy14>; + phy-mode = "internal"; + }; + + port@7 { + reg = <15>; + label = "lan8"; + phy-handle = <&phy15>; + phy-mode = "internal"; + }; + + port@28 { + ethernet = <ðernet0>; + reg = <28>; + phy-mode = "internal"; + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; +}; diff --git a/target/linux/rtl838x/dts/rtl838x.dtsi b/target/linux/rtl838x/dts/rtl838x.dtsi new file mode 100644 index 0000000000..63dc9bf1ba --- /dev/null +++ b/target/linux/rtl838x/dts/rtl838x.dtsi @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +/ { + #address-cells = <1>; + #size-cells = <1>; + + compatible = "realtek,rtl838x-soc"; + reg = <0xbb000000 0xa000>; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + frequency = <500000000>; + + cpu@0 { + compatible = "mips,mips4KEc"; + reg = <0>; + }; + }; + + memory@0 { + device_type = "memory"; + reg = <0x0 0x8000000>; + }; + + chosen { + bootargs = "console=ttyS0,38400"; + }; + + cpuintc: cpuintc { + #address-cells = <0>; + #interrupt-cells = <1>; + interrupt-controller; + compatible = "rtl838x,icu"; + reg = <0xb8003000 0x20>; + }; + + spi0: spi@b8001200 { + status = "okay"; + + compatible = "realtek,rtl838x-nor"; + reg = <0xb8001200 0x100>; + + #address-cells = <1>; + #size-cells = <0>; + }; + + uart0: uart@b8002000 { + status = "disabled"; + + compatible = "ns16550a"; + reg = <0xb8002000 0x100>; + + clock-frequency = <200000000>; + interrupts = <31>; + + reg-io-width = <1>; + reg-shift = <2>; + fifo-size = <1>; + no-loopback-test; + }; + + uart1: uart@b8002100 { + status = "okay"; + + compatible = "ns16550a"; + reg = <0xb8002100 0x100>; + + clock-frequency = <200000000>; + + interrupt-parent = <&cpuintc>; + interrupts = <30>; + + reg-io-width = <1>; + reg-shift = <2>; + fifo-size = <1>; + no-loopback-test; + }; + + gpio0: gpio-controller@b8003500 { + compatible = "realtek,rtl838x-gpio"; + reg = <0xb8003500 0x20>; + gpio-controller; + #gpio-cells = <2>; + interrupt-parent = <&cpuintc>; + interrupts = <23>; + }; + + ethernet0: ethernet@bb00a300 { + status = "okay"; + + compatible = "realtek,rtl838x-eth"; + reg = <0xbb00a300 0x100>; + interrupt-parent = <&cpuintc>; + interrupts = <24>; + #interrupt-cells = <1>; + phy-mode = "internal"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + switch0: switch@bb000000 { + status = "okay"; + + compatible = "realtek,rtl838x-switch"; + }; +}; diff --git a/target/linux/rtl838x/files-5.4/arch/mips/include/asm/mach-rtl838x/ioremap.h b/target/linux/rtl838x/files-5.4/arch/mips/include/asm/mach-rtl838x/ioremap.h new file mode 100644 index 0000000000..e7a5bfaffc --- /dev/null +++ b/target/linux/rtl838x/files-5.4/arch/mips/include/asm/mach-rtl838x/ioremap.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef RTL838X_IOREMAP_H_ +#define RTL838X_IOREMAP_H_ + +static inline phys_addr_t fixup_bigphys_addr(phys_addr_t phys_addr, phys_addr_t size) +{ + return phys_addr; +} + +static inline int is_rtl838x_internal_registers(phys_addr_t offset) +{ + /* IO-Block */ + if (offset >= 0xb8000000 && offset < 0xb9000000) + return 1; + /* Switch block */ + if (offset >= 0xbb000000 && offset < 0xbc000000) + return 1; + return 0; +} + +static inline void __iomem *plat_ioremap(phys_addr_t offset, unsigned long size, + unsigned long flags) +{ + if (is_rtl838x_internal_registers(offset)) + return (void __iomem *)offset; + return NULL; +} + +static inline int plat_iounmap(const volatile void __iomem *addr) +{ + return is_rtl838x_internal_registers((unsigned long)addr); +} + +#endif diff --git a/target/linux/rtl838x/files-5.4/arch/mips/include/asm/mach-rtl838x/irq.h b/target/linux/rtl838x/files-5.4/arch/mips/include/asm/mach-rtl838x/irq.h new file mode 100644 index 0000000000..e821111c5d --- /dev/null +++ b/target/linux/rtl838x/files-5.4/arch/mips/include/asm/mach-rtl838x/irq.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __ASM_MACH_RTL838X_IRQ_H +#define __ASM_MACH_RTL838X_IRQ_H + +#define MIPS_CPU_IRQ_BASE 0 +#define NR_IRQS 64 + +#include_next + +#endif /* __ASM_MACH_ATH79_IRQ_H */ diff --git a/target/linux/rtl838x/files-5.4/arch/mips/include/asm/mach-rtl838x/mach-rtl838x.h b/target/linux/rtl838x/files-5.4/arch/mips/include/asm/mach-rtl838x/mach-rtl838x.h new file mode 100644 index 0000000000..cece36635c --- /dev/null +++ b/target/linux/rtl838x/files-5.4/arch/mips/include/asm/mach-rtl838x/mach-rtl838x.h @@ -0,0 +1,428 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2006-2012 Tony Wu (tonywu@realtek.com) + * Copyright (C) 2020 B. Koblitz + */ +#ifndef _MACH_RTL838X_H_ +#define _MACH_RTL838X_H_ + +/* + * Register access macros + */ + +#define RTL838X_SW_BASE ((volatile void *) 0xBB000000) + +#define rtl838x_r32(reg) __raw_readl(reg) +#define rtl838x_w32(val, reg) __raw_writel(val, reg) +#define rtl838x_w32_mask(clear, set, reg) rtl838x_w32((rtl838x_r32(reg) & ~(clear)) | (set), reg) + +#define sw_r32(reg) __raw_readl(RTL838X_SW_BASE + reg) +#define sw_w32(val, reg) __raw_writel(val, RTL838X_SW_BASE + reg) +#define sw_w32_mask(clear, set, reg) \ + sw_w32((sw_r32(reg) & ~(clear)) | (set), reg) + +#define sw_r64(reg) ((((u64)__raw_readl(RTL838X_SW_BASE + reg)) << 32) | \ + __raw_readl(RTL838X_SW_BASE + reg + 4)) + +#define sw_w64(val, reg) do { \ + __raw_writel((u32)((val) >> 32), RTL838X_SW_BASE + reg); \ + __raw_writel((u32)((val) & 0xffffffff), \ + RTL838X_SW_BASE + reg + 4); \ + } while (0) + +/* + * SPRAM + */ +#define RTL838X_ISPRAM_BASE 0x0 +#define RTL838X_DSPRAM_BASE 0x0 + +/* + * IRQ Controller + */ +#define RTL838X_IRQ_CPU_BASE 0 +#define RTL838X_IRQ_CPU_NUM 8 +#define RTL838X_IRQ_ICTL_BASE (RTL838X_IRQ_CPU_BASE + RTL838X_IRQ_CPU_NUM) +#define RTL838X_IRQ_ICTL_NUM 32 + +/* + * MIPS32R2 counter + */ +#define RTL838X_COMPARE_IRQ (RTL838X_IRQ_CPU_BASE + 7) + +/* + * ICTL + * Base address 0xb8003000UL + */ +#define RTL838X_ICTL1_IRQ (RTL838X_IRQ_CPU_BASE + 2) +#define RTL838X_ICTL2_IRQ (RTL838X_IRQ_CPU_BASE + 3) +#define RTL838X_ICTL3_IRQ (RTL838X_IRQ_CPU_BASE + 4) +#define RTL838X_ICTL4_IRQ (RTL838X_IRQ_CPU_BASE + 5) +#define RTL838X_ICTL5_IRQ (RTL838X_IRQ_CPU_BASE + 6) + +#define GIMR (0x00) +#define UART0_IE (1 << 31) +#define UART1_IE (1 << 30) +#define TC0_IE (1 << 29) +#define TC1_IE (1 << 28) +#define OCPTO_IE (1 << 27) +#define HLXTO_IE (1 << 26) +#define SLXTO_IE (1 << 25) +#define NIC_IE (1 << 24) +#define GPIO_ABCD_IE (1 << 23) +#define GPIO_EFGH_IE (1 << 22) +#define RTC_IE (1 << 21) +#define WDT_IP1_IE (1 << 19) +#define WDT_IP2_IE (1 << 18) + +#define GISR (0x04) +#define UART0_IP (1 << 31) +#define UART1_IP (1 << 30) +#define TC0_IP (1 << 29) +#define TC1_IP (1 << 28) +#define OCPTO_IP (1 << 27) +#define HLXTO_IP (1 << 26) +#define SLXTO_IP (1 << 25) +#define NIC_IP (1 << 24) +#define GPIO_ABCD_IP (1 << 23) +#define GPIO_EFGH_IP (1 << 22) +#define RTC_IP (1 << 21) +#define WDT_IP1_IP (1 << 19) +#define WDT_IP2_IP (1 << 18) + +#define IRR0 (0x08) +#define IRR0_SETTING ((UART0_RS << 28) | \ + (UART1_RS << 24) | \ + (TC0_RS << 20) | \ + (TC1_RS << 16) | \ + (OCPTO_RS << 12) | \ + (HLXTO_RS << 8) | \ + (SLXTO_RS << 4) | \ + (NIC_RS << 0) \ + ) + +#define IRR1 (0x0c) + +#define IRR1_SETTING ((GPIO_ABCD_RS << 28) | \ + (GPIO_EFGH_RS << 24) | \ + (RTC_RS << 20) | \ + (SWCORE_RS << 16) \ + ) + +#define IRR2 (0x10) +#define IRR2_SETTING 0 + +#define IRR3 (0x14) +#define IRR3_SETTING 0 + +/* Interrupt Routing Selection */ +#define UART0_RS 2 +#define UART1_RS 1 +#define TC0_RS 5 +#define TC1_RS 1 +#define OCPTO_RS 1 +#define HLXTO_RS 1 +#define SLXTO_RS 1 +#define NIC_RS 4 +#define GPIO_ABCD_RS 4 +#define GPIO_EFGH_RS 4 +#define RTC_RS 4 +#define SWCORE_RS 3 +#define WDT_IP1_RS 4 +#define WDT_IP2_RS 5 + +/* Interrupt IRQ Assignments */ +#define UART0_IRQ 31 +#define UART1_IRQ 30 +#define TC0_IRQ 29 +#define TC1_IRQ 28 +#define OCPTO_IRQ 27 +#define HLXTO_IRQ 26 +#define SLXTO_IRQ 25 +#define NIC_IRQ 24 +#define GPIO_ABCD_IRQ 23 +#define GPIO_EFGH_IRQ 22 +#define RTC_IRQ 21 +#define SWCORE_IRQ 20 +#define WDT_IP1_IRQ 19 +#define WDT_IP2_IRQ 18 + +#define SYSTEM_FREQ 200000000 +#define RTL838X_UART0_BASE ((volatile void *)(0xb8002000UL)) +#define RTL838X_UART0_BAUD 38400 /* ex. 19200 or 38400 or 57600 or 115200 */ +#define RTL838X_UART0_FREQ (SYSTEM_FREQ - RTL838X_UART0_BAUD * 24) +#define RTL838X_UART0_MAPBASE 0x18002000UL +#define RTL838X_UART0_MAPSIZE 0x100 +#define RTL838X_UART0_IRQ UART0_IRQ + +#define RTL838X_UART1_BASE ((volatile void *)(0xb8002100UL)) +#define RTL838X_UART1_BAUD 38400 /* ex. 19200 or 38400 or 57600 or 115200 */ +#define RTL838X_UART1_FREQ (SYSTEM_FREQ - RTL838X_UART1_BAUD * 24) +#define RTL838X_UART1_MAPBASE 0x18002100UL +#define RTL838X_UART1_MAPSIZE 0x100 +#define RTL838X_UART1_IRQ UART1_IRQ + +#define UART0_RBR (RTL838X_UART0_BASE + 0x000) +#define UART0_THR (RTL838X_UART0_BASE + 0x000) +#define UART0_DLL (RTL838X_UART0_BASE + 0x000) +#define UART0_IER (RTL838X_UART0_BASE + 0x004) +#define UART0_DLM (RTL838X_UART0_BASE + 0x004) +#define UART0_IIR (RTL838X_UART0_BASE + 0x008) +#define UART0_FCR (RTL838X_UART0_BASE + 0x008) +#define UART0_LCR (RTL838X_UART0_BASE + 0x00C) +#define UART0_MCR (RTL838X_UART0_BASE + 0x010) +#define UART0_LSR (RTL838X_UART0_BASE + 0x014) + +#define UART1_RBR (RTL838X_UART1_BASE + 0x000) +#define UART1_THR (RTL838X_UART1_BASE + 0x000) +#define UART1_DLL (RTL838X_UART1_BASE + 0x000) +#define UART1_IER (RTL838X_UART1_BASE + 0x004) +#define UART1_DLM (RTL838X_UART1_BASE + 0x004) +#define UART1_IIR (RTL838X_UART1_BASE + 0x008) +#define UART1_FCR (RTL838X_UART1_BASE + 0x008) + #define FCR_EN 0x01 + #define FCR_RXRST 0x02 + #define XRST 0x02 + #define FCR_TXRST 0x04 + #define TXRST 0x04 + #define FCR_DMA 0x08 + #define FCR_RTRG 0xC0 + #define CHAR_TRIGGER_01 0x00 + #define CHAR_TRIGGER_04 0x40 + #define CHAR_TRIGGER_08 0x80 + #define CHAR_TRIGGER_14 0xC0 +#define UART1_LCR (RTL838X_UART1_BASE + 0x00C) + #define LCR_WLN 0x03 + #define CHAR_LEN_5 0x00 + #define CHAR_LEN_6 0x01 + #define CHAR_LEN_7 0x02 + #define CHAR_LEN_8 0x03 + #define LCR_STB 0x04 + #define ONE_STOP 0x00 + #define TWO_STOP 0x04 + #define LCR_PEN 0x08 + #define PARITY_ENABLE 0x01 + #define PARITY_DISABLE 0x00 + #define LCR_EPS 0x30 + #define PARITY_ODD 0x00 + #define PARITY_EVEN 0x10 + #define PARITY_MARK 0x20 + #define PARITY_SPACE 0x30 + #define LCR_BRK 0x40 + #define LCR_DLAB 0x80 + #define DLAB 0x80 +#define UART1_MCR (RTL838X_UART1_BASE + 0x010) +#define UART1_LSR (RTL838X_UART1_BASE + 0x014) + #define LSR_DR 0x01 + #define RxCHAR_AVAIL 0x01 + #define LSR_OE 0x02 + #define LSR_PE 0x04 + #define LSR_FE 0x08 + #define LSR_BI 0x10 + #define LSR_THRE 0x20 + #define TxCHAR_AVAIL 0x00 + #define TxCHAR_EMPTY 0x20 + #define LSR_TEMT 0x40 + #define LSR_RFE 0x80 + +/* + * Timer/counter for 8390/80/28 TC & MP chip + */ +#define RTL838X_TIMER0_BASE ((volatile void *)(0xb8003100UL)) +#define RTL838X_TIMER0_IRQ RTL838X_TC0_EXT_IRQ + +#define RTL8390TC_TC1DATA (RTL838X_TIMER0_BASE + 0x04) +#define RTL8390TC_TCD_OFFSET 8 +#define RTL8390TC_TC0CNT (RTL838X_TIMER0_BASE + 0x08) +#define RTL8390TC_TC1CNT (RTL838X_TIMER0_BASE + 0x0C) +#define RTL8390TC_TCCNR (RTL838X_TIMER0_BASE + 0x10) +#define RTL8390TC_TC0EN (1 << 31) +#define RTL8390TC_TC0MODE_TIMER (1 << 30) +#define RTL8390TC_TC1EN (1 << 29) +#define RTL8390TC_TC1MODE_TIMER (1 << 28) +#define RTL8390TC_TCIR (RTL838X_TIMER0_BASE + 0x14) +#define RTL8390TC_TC0IE (1 << 31) +#define RTL8390TC_TC1IE (1 << 30) +#define RTL8390TC_TC0IP (1 << 29) +#define RTL8390TC_TC1IP (1 << 28) +#define RTL8390TC_CDBR (RTL838X_TIMER0_BASE + 0x18) +#define RTL8390TC_DIVF_OFFSET 16 +#define RTL8390TC_WDTCNR (RTL838X_TIMER0_BASE + 0x1C) + +#define RTL8390MP_TC1DATA (RTL838X_TIMER0_BASE + 0x10) +#define RTL8390MP_TC0CNT (RTL838X_TIMER0_BASE + 0x04) +#define RTL8390MP_TC1CNT (RTL838X_TIMER0_BASE + 0x14) +#define RTL8390MP_TC0CTL (RTL838X_TIMER0_BASE + 0x08) +#define RTL8390MP_TC1CTL (RTL838X_TIMER0_BASE + 0x18) +#define RTL8390MP_TCEN (1 << 28) +#define RTL8390MP_TCMODE_TIMER (1 << 24) +#define RTL8390MP_TCDIV_FACTOR (0xFFFF << 0) +#define RTL8390MP_TC0INT (RTL838X_TIMER0_BASE + 0xC) +#define RTL8390MP_TC1INT (RTL838X_TIMER0_BASE + 0x1C) +#define RTL8390MP_TCIE (1 << 20) +#define RTL8390MP_TCIP (1 << 16) +#define RTL8390MP_WDTCNR (RTL838X_TIMER0_BASE + 0x50) + +#define RTL8380MP_TC0DATA (RTL838X_TIMER0_BASE + 0x00) +#define RTL8380MP_TC1DATA (RTL838X_TIMER0_BASE + 0x10) +#define RTL8380MP_TC0CNT (RTL838X_TIMER0_BASE + 0x04) +#define RTL8380MP_TC1CNT (RTL838X_TIMER0_BASE + 0x14) +#define RTL8380MP_TC0CTL (RTL838X_TIMER0_BASE + 0x08) +#define RTL8380MP_TC1CTL (RTL838X_TIMER0_BASE + 0x18) +#define RTL8380MP_TCEN (1 << 28) +#define RTL8380MP_TCMODE_TIMER (1 << 24) +#define RTL8380MP_TCDIV_FACTOR (0xFFFF << 0) +#define RTL8380MP_TC0INT (RTL838X_TIMER0_BASE + 0xC) +#define RTL8380MP_TC1INT (RTL838X_TIMER0_BASE + 0x1C) +#define RTL8380MP_TCIE (1 << 20) +#define RTL8380MP_TCIP (1 << 16) +#define RTL8380MP_WDTCNR (RTL838X_TIMER0_BASE + 0x50) + +#define DIVISOR_RTL8390 55 +#define DIVISOR_RTL8380 2500 +#define DIVISOR_MAX 16834 + +/* + * Memory Controller + */ +#define MC_MCR 0xB8001000 +#define MC_MCR_VAL 0x00000000 + +#define MC_DCR 0xB8001004 +#define MC_DCR0_VAL 0x54480000 + +#define MC_DTCR 0xB8001008 +#define MC_DTCR_VAL 0xFFFF05C0 + +/* + * GPIO + */ +#define GPIO_CTRL_REG_BASE ((volatile void *) 0xb8003500) +#define RTL838X_GPIO_PABC_CNR (GPIO_CTRL_REG_BASE + 0x0) +#define RTL838X_GPIO_PABC_TYPE (GPIO_CTRL_REG_BASE + 0x04) +#define RTL838X_GPIO_PABC_DIR (GPIO_CTRL_REG_BASE + 0x8) +#define RTL838X_GPIO_PABC_DATA (GPIO_CTRL_REG_BASE + 0xc) +#define RTL838X_GPIO_PABC_ISR (GPIO_CTRL_REG_BASE + 0x10) +#define RTL838X_GPIO_PAB_IMR (GPIO_CTRL_REG_BASE + 0x14) +#define RTL838X_GPIO_PC_IMR (GPIO_CTRL_REG_BASE + 0x18) + +#define RTL838X_MODEL_NAME_INFO (0x00D4) +#define RTL839X_MODEL_NAME_INFO (0x0FF0) +#define RTL838X_LED_GLB_CTRL (0xA000) +#define RTL839X_LED_GLB_CTRL (0x00E4) +#define RTL838X_EXT_GPIO_DIR_0 (0xA08C) +#define RTL838X_EXT_GPIO_DIR_1 (0xA090) +#define RTL838X_EXT_GPIO_DATA_0 (0xA094) +#define RTL838X_EXT_GPIO_DATA_1 (0xA098) +#define RTL838X_EXT_GPIO_INDRT_ACCESS (0xA09C) +#define RTL838X_EXTRA_GPIO_CTRL (0xA0E0) +#define RTL838X_EXTRA_GPIO_DIR_0 (0xA0E4) +#define RTL838X_EXTRA_GPIO_DIR_1 (0xA0E8) +#define RTL838X_EXTRA_GPIO_DATA_0 (0xA0EC) +#define RTL838X_EXTRA_GPIO_DATA_1 (0xA0F0) +#define RTL838X_DMY_REG5 (0x0144) +#define RTL838X_EXTRA_GPIO_CTRL (0xA0E0) + +#define RTL838X_GMII_INTF_SEL (0x1000) +#define RTL838X_IO_DRIVING_ABILITY_CTRL (0x1010) + +#define RTL838X_GPIO_A7 31 +#define RTL838X_GPIO_A6 30 +#define RTL838X_GPIO_A5 29 +#define RTL838X_GPIO_A4 28 +#define RTL838X_GPIO_A3 27 +#define RTL838X_GPIO_A2 26 +#define RTL838X_GPIO_A1 25 +#define RTL838X_GPIO_A0 24 +#define RTL838X_GPIO_B7 23 +#define RTL838X_GPIO_B6 22 +#define RTL838X_GPIO_B5 21 +#define RTL838X_GPIO_B4 20 +#define RTL838X_GPIO_B3 19 +#define RTL838X_GPIO_B2 18 +#define RTL838X_GPIO_B1 17 +#define RTL838X_GPIO_B0 16 +#define RTL838X_GPIO_C7 15 +#define RTL838X_GPIO_C6 14 +#define RTL838X_GPIO_C5 13 +#define RTL838X_GPIO_C4 12 +#define RTL838X_GPIO_C3 11 +#define RTL838X_GPIO_C2 10 +#define RTL838X_GPIO_C1 9 +#define RTL838X_GPIO_C0 8 + +#define RTL838X_INT_RW_CTRL (0x0058) +#define RTL838X_EXT_VERSION (0x00D0) +#define RTL838X_PLL_CML_CTRL (0x0FF8) +#define RTL838X_STRAP_DBG (0x100C) + +/* + * Reset + */ +#define RGCR (0x1E70) +#define RTL839X_RST_GLB_CTRL (0x0014) +#define RTL838X_RST_GLB_CTRL_1 (0x0040) + +/* LED control by switch */ +#define RTL838X_LED_MODE_SEL (0x1004) +#define RTL838X_LED_MODE_CTRL (0xA004) +#define RTL838X_LED_P_EN_CTRL (0xA008) + +/* LED control by software */ +#define RTL838X_LED_SW_CTRL (0xA00C) +#define RTL838X_LED0_SW_P_EN_CTRL (0xA010) +#define RTL838X_LED1_SW_P_EN_CTRL (0xA014) +#define RTL838X_LED2_SW_P_EN_CTRL (0xA018) +#define RTL838X_LED_SW_P_CTRL(p) (0xA01C + ((p) << 2)) + +#define RTL839X_MAC_EFUSE_CTRL (0x02ac) + +/* + * MDIO via Realtek's SMI interface + */ +#define RTL838X_SMI_GLB_CTRL (0xa100) +#define RTL838X_SMI_ACCESS_PHY_CTRL_0 (0xa1b8) +#define RTL838X_SMI_ACCESS_PHY_CTRL_1 (0xa1bc) +#define RTL838X_SMI_ACCESS_PHY_CTRL_2 (0xa1c0) +#define RTL838X_SMI_ACCESS_PHY_CTRL_3 (0xa1c4) +#define RTL838X_SMI_PORT0_5_ADDR_CTRL (0xa1c8) +#define RTL838X_SMI_POLL_CTRL (0xa17c) + +#define RTL839X_SMI_GLB_CTRL (0x03f8) +#define RTL839X_SMI_PORT_POLLING_CTRL (0x03fc) +#define RTL839X_PHYREG_ACCESS_CTRL (0x03DC) +#define RTL839X_PHYREG_CTRL (0x03E0) +#define RTL839X_PHYREG_PORT_CTRL(p) (0x03E4 + ((p >> 5) << 2)) +#define RTL839X_PHYREG_DATA_CTRL (0x03F0) + +/* + * Switch interrupts + */ +#define RTL838X_IMR_GLB (0x1100) +#define RTL838X_IMR_PORT_LINK_STS_CHG (0x1104) +#define RTL838X_ISR_GLB_SRC (0x1148) +#define RTL838X_ISR_PORT_LINK_STS_CHG (0x114C) +#define RTL839X_IMR_GLB (0x0064) +#define RTL839X_IMR_PORT_LINK_STS_CHG (0x0068) +#define RTL839X_ISR_GLB_SRC (0x009c) +#define RTL839X_ISR_PORT_LINK_STS_CHG (0x00a0) + +/* Definition of family IDs */ +#define RTL8389_FAMILY_ID (0x8389) +#define RTL8328_FAMILY_ID (0x8328) +#define RTL8390_FAMILY_ID (0x8390) +#define RTL8350_FAMILY_ID (0x8350) +#define RTL8380_FAMILY_ID (0x8380) +#define RTL8330_FAMILY_ID (0x8330) + +struct rtl838x_soc_info { + unsigned char *name; + unsigned int id; + unsigned int family; + unsigned char *compatible; + volatile void *sw_base; + volatile void *icu_base; +}; + +void rtl838x_soc_detect(struct rtl838x_soc_info *i); + +#endif /* _MACH_RTL838X_H_ */ diff --git a/target/linux/rtl838x/files-5.4/arch/mips/rtl838x/Makefile b/target/linux/rtl838x/files-5.4/arch/mips/rtl838x/Makefile new file mode 100644 index 0000000000..fe5e1d96a9 --- /dev/null +++ b/target/linux/rtl838x/files-5.4/arch/mips/rtl838x/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the rtl838x specific parts of the kernel +# + +obj-y := serial.o setup.o prom.o irq.o diff --git a/target/linux/rtl838x/files-5.4/arch/mips/rtl838x/Platform b/target/linux/rtl838x/files-5.4/arch/mips/rtl838x/Platform new file mode 100644 index 0000000000..4d48932d80 --- /dev/null +++ b/target/linux/rtl838x/files-5.4/arch/mips/rtl838x/Platform @@ -0,0 +1,6 @@ +# +# Realtek RTL838x SoCs +# +platform-$(CONFIG_RTL838X) += rtl838x/ +cflags-$(CONFIG_RTL838X) += -I$(srctree)/arch/mips/include/asm/mach-rtl838x/ +load-$(CONFIG_RTL838X) += 0xffffffff80000000 diff --git a/target/linux/rtl838x/files-5.4/arch/mips/rtl838x/irq.c b/target/linux/rtl838x/files-5.4/arch/mips/rtl838x/irq.c new file mode 100644 index 0000000000..34e90982b2 --- /dev/null +++ b/target/linux/rtl838x/files-5.4/arch/mips/rtl838x/irq.c @@ -0,0 +1,263 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Realtek RTL838X architecture specific IRQ handling + * + * Copyright (C) 2020 B. Koblitz + * based on the original BSP + * Copyright (C) 2006-2012 Tony Wu (tonywu@realtek.com) + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +extern struct rtl838x_soc_info soc_info; + +#define icu_r32(reg) rtl838x_r32(soc_info.icu_base + reg) +#define icu_w32(val, reg) rtl838x_w32(val, soc_info.icu_base + reg) +#define icu_w32_mask(clear, set, reg) rtl838x_w32_mask(clear, set, soc_info.icu_base + reg) + +static DEFINE_RAW_SPINLOCK(irq_lock); + +extern irqreturn_t c0_compare_interrupt(int irq, void *dev_id); +unsigned int rtl838x_ictl_irq_dispatch1(void); +unsigned int rtl838x_ictl_irq_dispatch2(void); +unsigned int rtl838x_ictl_irq_dispatch3(void); +unsigned int rtl838x_ictl_irq_dispatch4(void); +unsigned int rtl838x_ictl_irq_dispatch5(void); + +static struct irqaction irq_cascade1 = { + .handler = no_action, + .name = "RTL838X IRQ cascade1", +}; + +static struct irqaction irq_cascade2 = { + .handler = no_action, + .name = "RTL838X IRQ cascade2", +}; + +static struct irqaction irq_cascade3 = { + .handler = no_action, + .name = "RTL838X IRQ cascade3", +}; + +static struct irqaction irq_cascade4 = { + .handler = no_action, + .name = "RTL838X IRQ cascade4", +}; + +static struct irqaction irq_cascade5 = { + .handler = no_action, + .name = "RTL838X IRQ cascade5", +}; + +static void rtl838x_ictl_enable_irq(struct irq_data *i) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&irq_lock, flags); + icu_w32_mask(0, 1 << i->irq, GIMR); + raw_spin_unlock_irqrestore(&irq_lock, flags); +} + +static unsigned int rtl838x_ictl_startup_irq(struct irq_data *i) +{ + rtl838x_ictl_enable_irq(i); + return 0; +} + +static void rtl838x_ictl_disable_irq(struct irq_data *i) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&irq_lock, flags); + icu_w32_mask(1 << i->irq, 0, GIMR); + raw_spin_unlock_irqrestore(&irq_lock, flags); +} + +static void rtl838x_ictl_eoi_irq(struct irq_data *i) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&irq_lock, flags); + icu_w32_mask(0, 1 << i->irq, GIMR); + raw_spin_unlock_irqrestore(&irq_lock, flags); +} + +static struct irq_chip rtl838x_ictl_irq = { + .name = "RTL838X", + .irq_startup = rtl838x_ictl_startup_irq, + .irq_shutdown = rtl838x_ictl_disable_irq, + .irq_enable = rtl838x_ictl_enable_irq, + .irq_disable = rtl838x_ictl_disable_irq, + .irq_ack = rtl838x_ictl_disable_irq, + .irq_mask = rtl838x_ictl_disable_irq, + .irq_unmask = rtl838x_ictl_enable_irq, + .irq_eoi = rtl838x_ictl_eoi_irq, +}; + +/* + * RTL8390/80/28 Interrupt Scheme + * + * Source IRQ CPU INT + * -------- ------- ------- + * UART0 31 IP3 + * UART1 30 IP2 + * TIMER0 29 IP6 + * TIMER1 28 IP2 + * OCPTO 27 IP2 + * HLXTO 26 IP2 + * SLXTO 25 IP2 + * NIC 24 IP5 + * GPIO_ABCD 23 IP5 + * SWCORE 20 IP4 + */ + +unsigned int rtl838x_ictl_irq_dispatch1(void) +{ + /* Identify shared IRQ */ + unsigned int extint_ip = icu_r32(GIMR) & icu_r32(GISR); + + if (extint_ip & TC1_IP) + do_IRQ(TC1_IRQ); + else if (extint_ip & UART1_IP) + do_IRQ(UART1_IRQ); + else + spurious_interrupt(); + + return IRQ_HANDLED; +} + +unsigned int rtl838x_ictl_irq_dispatch2(void) +{ + do_IRQ(UART0_IRQ); + return IRQ_HANDLED; +} + +unsigned int rtl838x_ictl_irq_dispatch3(void) +{ + do_IRQ(SWCORE_IRQ); + return IRQ_HANDLED; +} + +unsigned int rtl838x_ictl_irq_dispatch4(void) +{ + /* Identify shared IRQ */ + unsigned int extint_ip = icu_r32(GIMR) & icu_r32(GISR); + + if (extint_ip & NIC_IP) + do_IRQ(NIC_IRQ); + else if (extint_ip & GPIO_ABCD_IP) + do_IRQ(GPIO_ABCD_IRQ); + else if ((extint_ip & GPIO_EFGH_IP) && (soc_info.family == RTL8328_FAMILY_ID)) + do_IRQ(GPIO_EFGH_IRQ); + else + spurious_interrupt(); + + return IRQ_HANDLED; +} + +unsigned int rtl838x_ictl_irq_dispatch5(void) +{ + do_IRQ(TC0_IRQ); + return IRQ_HANDLED; +} + +asmlinkage void plat_irq_dispatch(void) +{ + unsigned int pending; + + pending = read_c0_cause() & read_c0_status() & ST0_IM; + + if (pending & CAUSEF_IP7) + c0_compare_interrupt(7, NULL); + else if (pending & CAUSEF_IP6) + rtl838x_ictl_irq_dispatch5(); + else if (pending & CAUSEF_IP5) + rtl838x_ictl_irq_dispatch4(); + else if (pending & CAUSEF_IP4) + rtl838x_ictl_irq_dispatch3(); + else if (pending & CAUSEF_IP3) + rtl838x_ictl_irq_dispatch2(); + else if (pending & CAUSEF_IP2) + rtl838x_ictl_irq_dispatch1(); + else + spurious_interrupt(); +} + +static void __init rtl838x_ictl_irq_init(unsigned int irq_base) +{ + int i; + + for (i = 0; i < RTL838X_IRQ_ICTL_NUM; i++) + irq_set_chip_and_handler(irq_base + i, &rtl838x_ictl_irq, handle_level_irq); + + setup_irq(RTL838X_ICTL1_IRQ, &irq_cascade1); + setup_irq(RTL838X_ICTL2_IRQ, &irq_cascade2); + setup_irq(RTL838X_ICTL3_IRQ, &irq_cascade3); + setup_irq(RTL838X_ICTL4_IRQ, &irq_cascade4); + setup_irq(RTL838X_ICTL5_IRQ, &irq_cascade5); + + /* Set GIMR, IRR */ + icu_w32(TC0_IE | UART0_IE, GIMR); + icu_w32(IRR0_SETTING, IRR0); + icu_w32(IRR1_SETTING, IRR1); + icu_w32(IRR2_SETTING, IRR2); + icu_w32(IRR3_SETTING, IRR3); +} + +static int intc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) +{ + irq_set_chip_and_handler(hw, &rtl838x_ictl_irq, handle_level_irq); + + return 0; +} + +static const struct irq_domain_ops irq_domain_ops = { + .xlate = irq_domain_xlate_onecell, + .map = intc_map, +}; + +int __init icu_of_init(struct device_node *node, struct device_node *parent) +{ + int i; + struct irq_domain *domain; + struct resource res; + + pr_info("Found Interrupt controller: %s (%s)\n", node->name, node->full_name); + if (of_address_to_resource(node, 0, &res)) { + panic("Failed to get icu memory range"); + } + if (!request_mem_region(res.start, resource_size(&res), res.name)) + pr_err("Failed to request icu memory\n"); + soc_info.icu_base = ioremap(res.start, resource_size(&res)); + pr_info("ICU Memory: %08x\n", (u32)soc_info.icu_base); + + mips_cpu_irq_init(); + + domain = irq_domain_add_simple(node, 32, 0, &irq_domain_ops, NULL); + + /* Setup all external HW irqs */ + for (i = 8; i < 32; i++) + irq_domain_associate(domain, i, i); + + rtl838x_ictl_irq_init(RTL838X_IRQ_ICTL_BASE); + return 0; +} + +void __init arch_init_irq(void) +{ + /* do board-specific irq initialization */ + irqchip_init(); +} + +IRQCHIP_DECLARE(mips_cpu_intc, "rtl838x,icu", icu_of_init); diff --git a/target/linux/rtl838x/files-5.4/arch/mips/rtl838x/prom.c b/target/linux/rtl838x/files-5.4/arch/mips/rtl838x/prom.c new file mode 100644 index 0000000000..c1678b8785 --- /dev/null +++ b/target/linux/rtl838x/files-5.4/arch/mips/rtl838x/prom.c @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * prom.c + * Early intialization code for the Realtek RTL838X SoC + * + * based on the original BSP by + * Copyright (C) 2006-2012 Tony Wu (tonywu@realtek.com) + * Copyright (C) 2020 B. Koblitz + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +extern char arcs_cmdline[]; +const void *fdt; +extern const char __appended_dtb; +//extern int __init rtl838x_serial_init(void); + +void prom_console_init(void) +{ + /* UART 16550A is initialized by the bootloader */ +} + +#ifdef CONFIG_EARLY_PRINTK +#define rtl838x_r8(reg) __raw_readb(reg) +#define rtl838x_w8(val, reg) __raw_writeb(val, reg) + +void unregister_prom_console(void) +{ + +} + +void disable_early_printk(void) +{ + +} + +void prom_putchar(char c) +{ + unsigned int retry = 0; + + do { + if (retry++ >= 30000) { + /* Reset Tx FIFO */ + rtl838x_w8(TXRST | CHAR_TRIGGER_14, UART0_FCR); + return; + } + } while ((rtl838x_r8(UART0_LSR) & LSR_THRE) == TxCHAR_AVAIL); + + /* Send Character */ + rtl838x_w8(c, UART0_THR); +} + +char prom_getchar(void) +{ + return '\0'; +} +#endif + +struct rtl838x_soc_info soc_info; + +const char *get_system_type(void) +{ + return soc_info.name; +} + + +void __init prom_free_prom_memory(void) +{ + +} + +void __init device_tree_init(void) +{ + pr_info("%s called\r\n", __func__); + if (!fdt_check_header(&__appended_dtb)) { + fdt = &__appended_dtb; + pr_info("Using appended Device Tree.\n"); + } + initial_boot_params = (void *)fdt; + unflatten_and_copy_device_tree(); +} + +static void __init prom_init_cmdline(void) +{ + int argc = fw_arg0; + char **argv = (char **) KSEG1ADDR(fw_arg1); + int i; + + arcs_cmdline[0] = '\0'; + + for (i = 0; i < argc; i++) { + char *p = (char *) KSEG1ADDR(argv[i]); + + if (CPHYSADDR(p) && *p) { + strlcat(arcs_cmdline, p, sizeof(arcs_cmdline)); + strlcat(arcs_cmdline, " ", sizeof(arcs_cmdline)); + } + } + pr_info("Kernel command line: %s\n", arcs_cmdline); +} + +/* Do basic initialization */ +void __init prom_init(void) +{ + uint32_t model; + + pr_info("%s called\n", __func__); + soc_info.sw_base = RTL838X_SW_BASE; + + model = sw_r32(RTL838X_MODEL_NAME_INFO); + pr_info("RTL838X model is %x\n", model); + model = model >> 16 & 0xFFFF; + + if ((model != 0x8328) && (model != 0x8330) && (model != 0x8332) + && (model != 0x8380) && (model != 0x8382)) { + model = sw_r32(RTL839X_MODEL_NAME_INFO); + pr_info("RTL839X model is %x\n", model); + model = model >> 16 & 0xFFFF; + } + + soc_info.id = model; + + switch (model) { + case 0x8328: + soc_info.name = "RTL8328"; + soc_info.family = RTL8328_FAMILY_ID; + break; + case 0x8332: + soc_info.name = "RTL8332"; + soc_info.family = RTL8380_FAMILY_ID; + break; + case 0x8380: + soc_info.name = "RTL8380"; + soc_info.family = RTL8380_FAMILY_ID; + break; + case 0x8382: + soc_info.name = "RTL8382"; + soc_info.family = RTL8380_FAMILY_ID; + break; + case 0x8390: + soc_info.name = "RTL8390"; + soc_info.family = RTL8390_FAMILY_ID; + break; + case 0x8391: + soc_info.name = "RTL8391"; + soc_info.family = RTL8390_FAMILY_ID; + break; + case 0x8392: + soc_info.name = "RTL8392"; + soc_info.family = RTL8390_FAMILY_ID; + break; + case 0x8393: + soc_info.name = "RTL8393"; + soc_info.family = RTL8390_FAMILY_ID; + break; + default: + soc_info.name = "DEFAULT"; + soc_info.family = 0; + } + pr_info("SoC Type: %s\n", get_system_type()); + prom_init_cmdline(); +} diff --git a/target/linux/rtl838x/files-5.4/arch/mips/rtl838x/serial.c b/target/linux/rtl838x/files-5.4/arch/mips/rtl838x/serial.c new file mode 100644 index 0000000000..8a85933baf --- /dev/null +++ b/target/linux/rtl838x/files-5.4/arch/mips/rtl838x/serial.c @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * 8250 serial console setup for the Realtek RTL838X SoC + * + * based on the original BSP by + * Copyright (C) 2006-2012 Tony Wu (tonywu@realtek.com) + * + * Copyright (C) 2020 B. Koblitz + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +extern char arcs_cmdline[]; +extern struct rtl838x_soc_info soc_info; + +int __init rtl838x_serial_init(void) +{ +#ifdef CONFIG_SERIAL_8250 + int ret; + struct uart_port p; + unsigned long baud = 0; + int err; + char parity = '\0', bits = '\0', flow = '\0'; + char *s; + struct device_node *dn; + + dn = of_find_compatible_node(NULL, NULL, "ns16550a"); + if (dn) { + pr_info("Found NS16550a: %s (%s)\n", dn->name, dn->full_name); + dn = of_find_compatible_node(dn, NULL, "ns16550a"); + if (dn && of_device_is_available(dn) && soc_info.family == RTL8380_FAMILY_ID) { + /* Enable UART1 on RTL838x */ + pr_info("Enabling uart1\n"); + sw_w32(0x10, RTL838X_GMII_INTF_SEL); + } + } else { + pr_err("No NS16550a UART found!"); + return -ENODEV; + } + + s = strstr(arcs_cmdline, "console=ttyS0,"); + if (s) { + s += 14; + baud = kstrtoul(s, 10, &baud); + if (err) + baud = 0; + while (isdigit(*s)) + s++; + if (*s == ',') + s++; + if (*s) + parity = *s++; + if (*s == ',') + s++; + if (*s) + bits = *s++; + if (*s == ',') + s++; + if (*s == 'h') + flow = 'r'; + } + + if (baud == 0) { + baud = 38400; + pr_warn("Using default baud rate: %lu\n", baud); + } + if (parity != 'n' && parity != 'o' && parity != 'e') + parity = 'n'; + if (bits != '7' && bits != '8') + bits = '8'; + + memset(&p, 0, sizeof(p)); + + p.type = PORT_16550A; + p.membase = (unsigned char *) RTL838X_UART0_BASE; + p.irq = RTL838X_UART0_IRQ; + p.uartclk = SYSTEM_FREQ - (24 * baud); + p.flags = UPF_SKIP_TEST | UPF_LOW_LATENCY | UPF_FIXED_TYPE; + p.iotype = UPIO_MEM; + p.regshift = 2; + p.fifosize = 1; + + /* Call early_serial_setup() here, to set up 8250 console driver */ + if (early_serial_setup(&p) != 0) + ret = 1; +#endif + return 0; +} diff --git a/target/linux/rtl838x/files-5.4/arch/mips/rtl838x/setup.c b/target/linux/rtl838x/files-5.4/arch/mips/rtl838x/setup.c new file mode 100644 index 0000000000..f59481f76d --- /dev/null +++ b/target/linux/rtl838x/files-5.4/arch/mips/rtl838x/setup.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Setup for the Realtek RTL838X SoC: + * Memory, Timer and Serial + * + * Copyright (C) 2020 B. Koblitz + * based on the original BSP by + * Copyright (C) 2006-2012 Tony Wu (tonywu@realtek.com) + * + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include /* for mips_hpt_frequency */ +#include +#include + +#include "mach-rtl838x.h" + +extern int rtl838x_serial_init(void); +extern struct rtl838x_soc_info soc_info; + +struct clk { + struct clk_lookup cl; + unsigned long rate; +}; + +struct clk cpu_clk; + +u32 pll_reset_value; + +static void rtl838x_restart(char *command) +{ + u32 pll = sw_r32(RTL838X_PLL_CML_CTRL); + /* SoC reset vector (in flash memory): on RTL839x platform preferred way to reset */ + void (*f)(void) = (void *) 0xbfc00000; + + pr_info("System restart.\n"); + if (soc_info.family == RTL8390_FAMILY_ID) { + f(); + /* If calling reset vector fails, reset entire chip */ + sw_w32(0xFFFFFFFF, RTL839X_RST_GLB_CTRL); + /* If this fails, halt the CPU */ + while + (1); + } + + pr_info("PLL control register: %x, applying reset value %x\n", + pll, pll_reset_value); + sw_w32(3, RTL838X_INT_RW_CTRL); + sw_w32(pll_reset_value, RTL838X_PLL_CML_CTRL); + sw_w32(0, RTL838X_INT_RW_CTRL); + + pr_info("Resetting RTL838X SoC\n"); + /* Reset Global Control1 Register */ + sw_w32(1, RTL838X_RST_GLB_CTRL_1); +} + +static void rtl838x_halt(void) +{ + pr_info("System halted.\n"); + while + (1); +} + +static void __init rtl838x_setup(void) +{ + unsigned int val; + + pr_info("Registering _machine_restart\n"); + _machine_restart = rtl838x_restart; + _machine_halt = rtl838x_halt; + + val = rtl838x_r32((volatile void *)0xBB0040000); + if (val == 3) + pr_info("PCI device found\n"); + else + pr_info("NO PCI device found\n"); + + /* Setup System LED. Bit 15 (14 for RTL8390) then allows to toggle it */ + if (soc_info.family == RTL8380_FAMILY_ID) + sw_w32_mask(0, 3 << 16, RTL838X_LED_GLB_CTRL); + else + sw_w32_mask(0, 3 << 15, RTL839X_LED_GLB_CTRL); +} + +void __init plat_mem_setup(void) +{ + void *dtb; + + pr_info("%s called\n", __func__); + + set_io_port_base(KSEG1); + + if (fw_passed_dtb) /* UHI interface */ + dtb = (void *)fw_passed_dtb; + else if (__dtb_start != __dtb_end) + dtb = (void *)__dtb_start; + else + panic("no dtb found"); + + /* + * Load the devicetree. This causes the chosen node to be + * parsed resulting in our memory appearing + */ + __dt_setup_arch(dtb); + + rtl838x_setup(); +} + + +/* + * Linux clock API + */ +int clk_enable(struct clk *clk) +{ + return 0; +} +EXPORT_SYMBOL_GPL(clk_enable); + +void clk_disable(struct clk *clk) +{ + +} +EXPORT_SYMBOL_GPL(clk_disable); + +unsigned long clk_get_rate(struct clk *clk) +{ + if (!clk) + return 0; + + return clk->rate; +} +EXPORT_SYMBOL_GPL(clk_get_rate); + +int clk_set_rate(struct clk *clk, unsigned long rate) +{ + return -1; +} +EXPORT_SYMBOL_GPL(clk_set_rate); + +long clk_round_rate(struct clk *clk, unsigned long rate) +{ + return -1; +} +EXPORT_SYMBOL_GPL(clk_round_rate); + +void __init plat_time_init(void) +{ + u32 freq = 500000000; + struct device_node *np; + struct clk *clk = &cpu_clk; + + np = of_find_node_by_name(NULL, "cpus"); + if (!np) { + pr_err("Missing 'cpus' DT node, using default frequency."); + } else { + if (of_property_read_u32(np, "frequency", &freq) < 0) + pr_err("No 'frequency' property in DT, using default."); + else + pr_info("CPU frequency from device tree: %d", freq); + of_node_put(np); + } + + clk->rate = freq; + + if (IS_ERR(clk)) + panic("unable to get CPU clock, err=%ld", PTR_ERR(clk)); + + pr_info("CPU Clock: %ld MHz\n", clk->rate / 1000000); + mips_hpt_frequency = freq / 2; + + pll_reset_value = sw_r32(RTL838X_PLL_CML_CTRL); + pr_info("PLL control register: %x\n", pll_reset_value); + + /* With the info from the command line and cpu-freq we can setup the console */ + rtl838x_serial_init(); +} diff --git a/target/linux/rtl838x/files-5.4/drivers/gpio/gpio-rtl838x.c b/target/linux/rtl838x/files-5.4/drivers/gpio/gpio-rtl838x.c new file mode 100644 index 0000000000..9b1cd3d70c --- /dev/null +++ b/target/linux/rtl838x/files-5.4/drivers/gpio/gpio-rtl838x.c @@ -0,0 +1,808 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include + +/* RTL8231 registers for LED control */ +#define RTL8231_LED_FUNC0 0x0000 +#define RTL8231_GPIO_PIN_SEL(gpio) ((0x0002) + ((gpio) >> 4)) +#define RTL8231_GPIO_DIR(gpio) ((0x0005) + ((gpio) >> 4)) +#define RTL8231_GPIO_DATA(gpio) ((0x001C) + ((gpio) >> 4)) +#define RTL8231_GPIO_PIN_SEL0 0x0002 +#define RTL8231_GPIO_PIN_SEL1 0x0003 +#define RTL8231_GPIO_PIN_SEL2 0x0004 +#define RTL8231_GPIO_IO_SEL0 0x0005 +#define RTL8231_GPIO_IO_SEL1 0x0006 +#define RTL8231_GPIO_IO_SEL2 0x0007 + +#define MDC_WAIT { int i; for (i = 0; i < 2; i++); } +#define I2C_WAIT { int i; for (i = 0; i < 5; i++); } + +struct rtl838x_gpios { + struct gpio_chip gc; + u32 id; + struct device *dev; + int irq; + int bus_id; + int num_leds; + int min_led; + int leds_per_port; + u32 led_mode; + u16 rtl8381_phy_id; + int smi_clock; + int smi_data; + int i2c_sda; + int i2c_sdc; +}; + +extern struct mutex smi_lock; + +u32 rtl838x_rtl8231_read(u8 bus_id, u32 reg) +{ + u32 t = 0; + + reg &= 0x1f; + bus_id &= 0x1f; + + /* Calculate read register address */ + t = (bus_id << 2) | (reg << 7); + + mutex_lock(&smi_lock); + /* Set execution bit: cleared when operation completed */ + t |= 1; + sw_w32(t, RTL838X_EXT_GPIO_INDRT_ACCESS); + do { /* TODO: Return 0x80000000 if timeout */ + t = sw_r32(RTL838X_EXT_GPIO_INDRT_ACCESS); + } while (t & 1); + pr_debug("%s: %x, %x, %x\n", __func__, bus_id, reg, (t & 0xffff0000) >> 16); + + mutex_unlock(&smi_lock); + return (t & 0xffff0000) >> 16; +} + +int rtl838x_rtl8231_write(u8 bus_id, u32 reg, u32 data) +{ + u32 t = 0; + + pr_debug("%s: %x, %x, %x\n", __func__, bus_id, reg, data); + data &= 0xffff; + reg &= 0x1f; + bus_id &= 0x1f; + + mutex_lock(&smi_lock); + t = (bus_id << 2) | (reg << 7) | (data << 16); + /* Set write bit */ + t |= 2; + + /* Set execution bit: cleared when operation completed */ + t |= 1; + sw_w32(t, RTL838X_EXT_GPIO_INDRT_ACCESS); + do { /* TODO: Return -1 if timeout */ + t = sw_r32(RTL838X_EXT_GPIO_INDRT_ACCESS); + } while (t & 1); + + mutex_unlock(&smi_lock); + return 0; +} + +static int rtl8231_pin_dir(u8 bus_id, u32 gpio, u32 dir) +{ + /* dir 1: input + * dir 0: output + */ + + u32 v; + int pin_sel_addr = RTL8231_GPIO_PIN_SEL(gpio); + int pin_dir_addr = RTL8231_GPIO_DIR(gpio); + int pin = gpio % 16; + int dpin = pin; + + if (gpio > 31) { + dpin = pin << 5; + pin_dir_addr = pin_sel_addr; + } + + /* Select GPIO function for pin */ + v = rtl838x_rtl8231_read(bus_id, pin_sel_addr); + if (v & 0x80000000) { + pr_err("Error reading RTL8231\n"); + return -1; + } + rtl838x_rtl8231_write(bus_id, pin_sel_addr, v | (1 << pin)); + + v = rtl838x_rtl8231_read(bus_id, pin_dir_addr); + if (v & 0x80000000) { + pr_err("Error reading RTL8231\n"); + return -1; + } + rtl838x_rtl8231_write(bus_id, pin_dir_addr, + (v & ~(1 << dpin)) | (dir << dpin)); + return 0; +} + +static int rtl8231_pin_dir_get(u8 bus_id, u32 gpio, u32 *dir) +{ + /* dir 1: input + * dir 0: output + */ + + u32 v; + int pin_dir_addr = RTL8231_GPIO_DIR(gpio); + int pin = gpio % 16; + + if (gpio > 31) { + pin_dir_addr = RTL8231_GPIO_PIN_SEL(gpio); + pin = pin << 5; + } + + v = rtl838x_rtl8231_read(bus_id, pin_dir_addr); + if (v & (1 << pin)) + *dir = 1; + else + *dir = 0; + return 0; +} + +static int rtl8231_pin_set(u8 bus_id, u32 gpio, u32 data) +{ + u32 v = rtl838x_rtl8231_read(bus_id, RTL8231_GPIO_DATA(gpio)); + + if (v & 0x80000000) { + pr_err("Error reading RTL8231\n"); + return -1; + } + rtl838x_rtl8231_write(bus_id, RTL8231_GPIO_DATA(gpio), + (v & ~(1 << (gpio % 16))) | (data << (gpio % 16))); + return 0; +} + +static int rtl8231_pin_get(u8 bus_id, u32 gpio, u16 *state) +{ + u32 v = rtl838x_rtl8231_read(bus_id, RTL8231_GPIO_DATA(gpio)); + + if (v & 0x80000000) { + pr_err("Error reading RTL8231\n"); + return -1; + } + + *state = v & 0xffff; + return 0; +} + +static int rtl838x_direction_input(struct gpio_chip *gc, unsigned int offset) +{ + struct rtl838x_gpios *gpios = gpiochip_get_data(gc); + + pr_debug("%s: %d\n", __func__, offset); + + if (offset < 32) { + rtl838x_w32_mask(1 << offset, 0, RTL838X_GPIO_PABC_DIR); + return 0; + } + + /* Internal LED driver does not support input */ + if (offset >= 32 && offset < 64) + return -ENOTSUPP; + + if (offset >= 64 && offset < 100 && gpios->bus_id >= 0) + return rtl8231_pin_dir(gpios->bus_id, offset - 64, 1); + + return -ENOTSUPP; +} + +static int rtl838x_direction_output(struct gpio_chip *gc, unsigned int offset, int value) +{ + struct rtl838x_gpios *gpios = gpiochip_get_data(gc); + + pr_debug("%s: %d\n", __func__, offset); + if (offset < 32) + rtl838x_w32_mask(0, 1 << offset, RTL838X_GPIO_PABC_DIR); + + /* LED for PWR and SYS driver is direction output by default */ + if (offset >= 32 && offset < 64) + return 0; + + if (offset >= 64 && offset < 100 && gpios->bus_id >= 0) + return rtl8231_pin_dir(gpios->bus_id, offset - 64, 0); + return 0; +} + +static int rtl838x_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + u32 v = 0; + struct rtl838x_gpios *gpios = gpiochip_get_data(gc); + + pr_debug("%s: %d\n", __func__, offset); + if (offset < 32) { + v = rtl838x_r32(RTL838X_GPIO_PABC_DIR); + if (v & (1 << offset)) + return 0; + return 1; + } + + /* LED driver for PWR and SYS is direction output by default */ + if (offset >= 32 && offset < 64) + return 0; + + if (offset >= 64 && offset < 100 && gpios->bus_id >= 0) { + rtl8231_pin_dir_get(gpios->bus_id, offset - 64, &v); + return v; + } + + return 0; +} + +static int rtl838x_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ + u32 v; + u16 state = 0; + int bit; + struct rtl838x_gpios *gpios = gpiochip_get_data(gc); + + pr_debug("%s: %d\n", __func__, offset); + + /* Internal GPIO of the RTL8380 */ + if (offset < 32) { + v = rtl838x_r32(RTL838X_GPIO_PABC_DATA); + if (v & (1 << offset)) + return 1; + return 0; + } + + /* LED driver for PWR and SYS */ + if (offset >= 32 && offset < 64) { + v = sw_r32(RTL838X_LED_GLB_CTRL); + if (v & (1 << (offset-32))) + return 1; + return 0; + } + + /* Indirect access GPIO with RTL8231 */ + if (offset >= 64 && offset < 100 && gpios->bus_id >= 0) { + rtl8231_pin_get(gpios->bus_id, offset - 64, &state); + if (state & (1 << (offset % 16))) + return 1; + return 0; + } + + bit = (offset - 100) % 32; + if (offset >= 100 && offset < 132) { + if (sw_r32(RTL838X_LED1_SW_P_EN_CTRL) & (1 << bit)) + return 1; + return 0; + } + if (offset >= 132 && offset < 164) { + if (sw_r32(RTL838X_LED1_SW_P_EN_CTRL) & (1 << bit)) + return 1; + return 0; + } + if (offset >= 164 && offset < 196) { + if (sw_r32(RTL838X_LED1_SW_P_EN_CTRL) & (1 << bit)) + return 1; + return 0; + } + return 0; +} + +void rtl838x_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) +{ + int bit; + struct rtl838x_gpios *gpios = gpiochip_get_data(gc); + + pr_debug("rtl838x_set: %d, value: %d\n", offset, value); + /* Internal GPIO of the RTL8380 */ + if (offset < 32) { + if (value) + rtl838x_w32_mask(0, 1 << offset, RTL838X_GPIO_PABC_DATA); + else + rtl838x_w32_mask(1 << offset, 0, RTL838X_GPIO_PABC_DATA); + } + + /* LED driver for PWR and SYS */ + if (offset >= 32 && offset < 64) { + bit = offset - 32; + if (value) + sw_w32_mask(0, 1 << bit, RTL838X_LED_GLB_CTRL); + else + sw_w32_mask(1 << bit, 0, RTL838X_LED_GLB_CTRL); + return; + } + + /* Indirect access GPIO with RTL8231 */ + if (offset >= 64 && offset < 100 && gpios->bus_id >= 0) { + rtl8231_pin_set(gpios->bus_id, offset - 64, value); + return; + } + + bit = (offset - 100) % 32; + /* First Port-LED */ + if (offset >= 100 && offset < 132 + && offset >= (100 + gpios->min_led) + && offset < (100 + gpios->min_led + gpios->num_leds)) { + if (value) + sw_w32_mask(7, 5, RTL838X_LED_SW_P_CTRL(bit)); + else + sw_w32_mask(7, 0, RTL838X_LED_SW_P_CTRL(bit)); + } + if (offset >= 132 && offset < 164 + && offset >= (132 + gpios->min_led) + && offset < (132 + gpios->min_led + gpios->num_leds)) { + if (value) + sw_w32_mask(7 << 3, 5 << 3, RTL838X_LED_SW_P_CTRL(bit)); + else + sw_w32_mask(7 << 3, 0, RTL838X_LED_SW_P_CTRL(bit)); + } + if (offset >= 164 && offset < 196 + && offset >= (164 + gpios->min_led) + && offset < (164 + gpios->min_led + gpios->num_leds)) { + if (value) + sw_w32_mask(7 << 6, 5 << 6, RTL838X_LED_SW_P_CTRL(bit)); + else + sw_w32_mask(7 << 6, 0, RTL838X_LED_SW_P_CTRL(bit)); + } + __asm__ volatile ("sync"); +} + +int rtl8231_init(struct rtl838x_gpios *gpios) +{ + uint32_t v; + u8 bus_id = gpios->bus_id; + + pr_info("%s called\n", __func__); + + /* Enable RTL8231 indirect access mode */ + sw_w32_mask(0, 1, RTL838X_EXTRA_GPIO_CTRL); + sw_w32_mask(3, 1, RTL838X_DMY_REG5); + + /* Enable RTL8231 via GPIO_A1 line */ + rtl838x_w32_mask(0, 1 << RTL838X_GPIO_A1, RTL838X_GPIO_PABC_DIR); + rtl838x_w32_mask(0, 1 << RTL838X_GPIO_A1, RTL838X_GPIO_PABC_DATA); + mdelay(50); /* wait 50ms for reset */ + + /*Select GPIO functionality for pins 0-15, 16-31 and 32-37 */ + rtl838x_rtl8231_write(bus_id, RTL8231_GPIO_PIN_SEL(0), 0xffff); + rtl838x_rtl8231_write(bus_id, RTL8231_GPIO_PIN_SEL(16), 0xffff); + rtl838x_rtl8231_write(bus_id, RTL8231_GPIO_PIN_SEL2, 0x03ff); + + v = rtl838x_rtl8231_read(bus_id, RTL8231_LED_FUNC0); + pr_info("RTL8231 led function now: %x\n", v); + + return 0; +} + +static void smi_write_bit(struct rtl838x_gpios *gpios, u32 bit) +{ + if (bit) + rtl838x_w32_mask(0, 1 << gpios->smi_data, RTL838X_GPIO_PABC_DATA); + else + rtl838x_w32_mask(1 << gpios->smi_data, 0, RTL838X_GPIO_PABC_DATA); + + MDC_WAIT; + rtl838x_w32_mask(1 << gpios->smi_clock, 0, RTL838X_GPIO_PABC_DATA); + MDC_WAIT; + rtl838x_w32_mask(0, 1 << gpios->smi_clock, RTL838X_GPIO_PABC_DATA); +} + +static int smi_read_bit(struct rtl838x_gpios *gpios) +{ + u32 v; + + MDC_WAIT; + rtl838x_w32_mask(1 << gpios->smi_clock, 0, RTL838X_GPIO_PABC_DATA); + MDC_WAIT; + rtl838x_w32_mask(0, 1 << gpios->smi_clock, RTL838X_GPIO_PABC_DATA); + + v = rtl838x_r32(RTL838X_GPIO_PABC_DATA); + if (v & (1 << gpios->smi_data)) + return 1; + return 0; +} + +/* Tri-state of MDIO line */ +static void smi_z(struct rtl838x_gpios *gpios) +{ + /* MDIO pin to input */ + rtl838x_w32_mask(1 << gpios->smi_data, 0, RTL838X_GPIO_PABC_DIR); + MDC_WAIT; + rtl838x_w32_mask(1 << gpios->smi_clock, 0, RTL838X_GPIO_PABC_DATA); + MDC_WAIT; + rtl838x_w32_mask(0, 1 << gpios->smi_clock, RTL838X_GPIO_PABC_DATA); +} + +static void smi_write_bits(struct rtl838x_gpios *gpios, u32 data, int len) +{ + while (len) { + len--; + smi_write_bit(gpios, data & (1 << len)); + } +} + +static void smi_read_bits(struct rtl838x_gpios *gpios, int len, u32 *data) +{ + u32 v = 0; + + while (len) { + len--; + v <<= 1; + v |= smi_read_bit(gpios); + } + *data = v; +} + +/* Bit-banged verson of SMI write access, caller must hold smi_lock */ +int rtl8380_smi_write(struct rtl838x_gpios *gpios, u16 reg, u32 data) +{ + u16 bus_id = gpios->bus_id; + + /* Set clock and data pins on RTL838X to output */ + rtl838x_w32_mask(0, 1 << gpios->smi_clock, RTL838X_GPIO_PABC_DIR); + rtl838x_w32_mask(0, 1 << gpios->smi_data, RTL838X_GPIO_PABC_DIR); + + /* Write start bits */ + smi_write_bits(gpios, 0xffffffff, 32); + + smi_write_bits(gpios, 0x5, 4); /* ST and write OP */ + + smi_write_bits(gpios, bus_id, 5); /* 5 bits: phy address */ + smi_write_bits(gpios, reg, 5); /* 5 bits: register address */ + + smi_write_bits(gpios, 0x2, 2); /* TURNAROUND */ + + smi_write_bits(gpios, data, 16); /* 16 bits: data*/ + + smi_z(gpios); + + return 0; +} + +/* Bit-banged verson of SMI read access, caller must hold smi_lock */ +int rtl8380_smi_read(struct rtl838x_gpios *gpios, u16 reg, u32 *data) +{ + u16 bus_id = gpios->bus_id; + + /* Set clock and data pins on RTL838X to output */ + rtl838x_w32_mask(0, 1 << gpios->smi_clock, RTL838X_GPIO_PABC_DIR); + rtl838x_w32_mask(0, 1 << gpios->smi_data, RTL838X_GPIO_PABC_DIR); + + /* Write start bits */ + smi_write_bits(gpios, 0xffffffff, 32); + + smi_write_bits(gpios, 0x6, 4); /* ST and read OP */ + + smi_write_bits(gpios, bus_id, 5); /* 5 bits: phy address */ + smi_write_bits(gpios, reg, 5); /* 5 bits: register address */ + + smi_z(gpios); /* TURNAROUND */ + + smi_read_bits(gpios, 16, data); + return 0; +} + +static void i2c_pin_set(struct rtl838x_gpios *gpios, int pin, u32 data) +{ + u32 v; + + rtl8380_smi_read(gpios, RTL8231_GPIO_DATA(pin), &v); + if (!data) + v &= ~(1 << (pin % 16)); + else + v |= (1 << (pin % 16)); + rtl8380_smi_write(gpios, RTL8231_GPIO_DATA(pin), v); +} + +static void i2c_pin_get(struct rtl838x_gpios *gpios, int pin, u32 *data) +{ + u32 v; + + rtl8380_smi_read(gpios, RTL8231_GPIO_DATA(pin), &v); + if (v & (1 << (pin % 16))) { + *data = 1; + return; + } + *data = 0; +} + +static void i2c_pin_dir(struct rtl838x_gpios *gpios, int pin, u16 direction) +{ + u32 v; + + rtl8380_smi_read(gpios, RTL8231_GPIO_DIR(pin), &v); + if (direction) // Output + v &= ~(1 << (pin % 16)); + else + v |= (1 << (pin % 16)); + rtl8380_smi_write(gpios, RTL8231_GPIO_DIR(pin), v); +} + +static void i2c_start(struct rtl838x_gpios *gpios) +{ + i2c_pin_dir(gpios, gpios->i2c_sda, 0); /* Output */ + i2c_pin_dir(gpios, gpios->i2c_sdc, 0); /* Output */ + I2C_WAIT; + i2c_pin_set(gpios, gpios->i2c_sdc, 1); + I2C_WAIT; + i2c_pin_set(gpios, gpios->i2c_sda, 1); + I2C_WAIT; + i2c_pin_set(gpios, gpios->i2c_sda, 0); + I2C_WAIT; + i2c_pin_set(gpios, gpios->i2c_sdc, 0); + I2C_WAIT; +} + +static void i2c_stop(struct rtl838x_gpios *gpios) +{ + I2C_WAIT; + i2c_pin_set(gpios, gpios->i2c_sdc, 1); + i2c_pin_set(gpios, gpios->i2c_sda, 0); + I2C_WAIT; + + i2c_pin_set(gpios, gpios->i2c_sda, 1); + I2C_WAIT; + i2c_pin_set(gpios, gpios->i2c_sdc, 0); + + i2c_pin_dir(gpios, gpios->i2c_sda, 1); /* Input */ + i2c_pin_dir(gpios, gpios->i2c_sdc, 1); /* Input */ +} + +static void i2c_read_bits(struct rtl838x_gpios *gpios, int len, u32 *data) +{ + u32 v = 0, t; + + while (len) { + len--; + v <<= 1; + i2c_pin_set(gpios, gpios->i2c_sdc, 1); + I2C_WAIT; + i2c_pin_get(gpios, gpios->i2c_sda, &t); + v |= t; + i2c_pin_set(gpios, gpios->i2c_sdc, 0); + I2C_WAIT; + } + *data = v; +} + +static void i2c_write_bits(struct rtl838x_gpios *gpios, u32 data, int len) +{ + while (len) { + len--; + i2c_pin_set(gpios, gpios->i2c_sda, data & (1 << len)); + I2C_WAIT; + i2c_pin_set(gpios, gpios->i2c_sdc, 1); + I2C_WAIT; + i2c_pin_set(gpios, gpios->i2c_sdc, 0); + I2C_WAIT; + } +} + +/* This initializes direct external GPIOs via the RTL8231 */ +int rtl8380_rtl8321_init(struct rtl838x_gpios *gpios) +{ + u32 v; + int mdc = gpios->smi_clock; + int mdio = gpios->smi_data; + + pr_info("Configuring SMI: Clock %d, Data %d\n", mdc, mdio); + sw_w32_mask(0, 0x2, RTL838X_IO_DRIVING_ABILITY_CTRL); + + /* Enter simulated GPIO mode */ + sw_w32_mask(1, 0, RTL838X_EXTRA_GPIO_CTRL); + + /* MDIO clock to 2.6MHz */ + sw_w32_mask(0x3 << 8, 0, RTL838X_EXTRA_GPIO_CTRL); + + /* Configure SMI clock and data GPIO pins */ + rtl838x_w32_mask((1 << mdc) | (1 << mdio), 0, RTL838X_GPIO_PABC_CNR); + rtl838x_w32_mask(0, (1 << mdc) | (1 << mdio), RTL838X_GPIO_PABC_DIR); + + rtl8380_smi_write(gpios, RTL8231_GPIO_PIN_SEL0, 0xffff); + rtl8380_smi_write(gpios, RTL8231_GPIO_PIN_SEL1, 0xffff); + rtl8380_smi_read(gpios, RTL8231_GPIO_PIN_SEL2, &v); + v |= 0x1f; + rtl8380_smi_write(gpios, RTL8231_GPIO_PIN_SEL2, v); + + rtl8380_smi_write(gpios, RTL8231_GPIO_IO_SEL0, 0xffff); + rtl8380_smi_write(gpios, RTL8231_GPIO_IO_SEL1, 0xffff); + rtl8380_smi_read(gpios, RTL8231_GPIO_IO_SEL2, &v); + v |= 0x1f << 5; + rtl8380_smi_write(gpios, RTL8231_GPIO_PIN_SEL2, v); + + return 0; +} + +void rtl8380_led_test(u32 mask) +{ + int i; + u32 mode_sel = sw_r32(RTL838X_LED_MODE_SEL); + u32 led_gbl = sw_r32(RTL838X_LED_GLB_CTRL); + u32 led_p_en = sw_r32(RTL838X_LED_P_EN_CTRL); + + /* 2 Leds for ports 0-23 and 24-27, 3 would be 0x7 */ + sw_w32_mask(0x3f, 0x3 | (0x3 << 3), RTL838X_LED_GLB_CTRL); + /* Enable all leds */ + sw_w32(0xFFFFFFF, RTL838X_LED_P_EN_CTRL); + + /* Enable software control of all leds */ + sw_w32(0xFFFFFFF, RTL838X_LED_SW_CTRL); + sw_w32(0xFFFFFFF, RTL838X_LED0_SW_P_EN_CTRL); + sw_w32(0xFFFFFFF, RTL838X_LED1_SW_P_EN_CTRL); + sw_w32(0x0000000, RTL838X_LED2_SW_P_EN_CTRL); + + for (i = 0; i < 28; i++) { + if (mask & (1 << i)) + sw_w32(5 | (5 << 3) | (5 << 6), + RTL838X_LED_SW_P_CTRL(i)); + } + msleep(3000); + + sw_w32(led_p_en, RTL838X_LED_P_EN_CTRL); + /* Disable software control of all leds */ + sw_w32(0x0000000, RTL838X_LED_SW_CTRL); + sw_w32(0x0000000, RTL838X_LED0_SW_P_EN_CTRL); + sw_w32(0x0000000, RTL838X_LED1_SW_P_EN_CTRL); + sw_w32(0x0000000, RTL838X_LED2_SW_P_EN_CTRL); + + sw_w32(led_gbl, RTL838X_LED_GLB_CTRL); + sw_w32(mode_sel, RTL838X_LED_MODE_SEL); +} + +void take_port_leds(struct rtl838x_gpios *gpios) +{ + int leds_per_port = gpios->leds_per_port; + int mode = gpios->led_mode; + + pr_info("%s, %d, %x\n", __func__, leds_per_port, mode); + pr_debug("Bootloader settings: %x %x %x\n", + sw_r32(RTL838X_LED0_SW_P_EN_CTRL), + sw_r32(RTL838X_LED1_SW_P_EN_CTRL), + sw_r32(RTL838X_LED2_SW_P_EN_CTRL) + ); + + pr_debug("led glb: %x, sel %x\n", + sw_r32(RTL838X_LED_GLB_CTRL), sw_r32(RTL838X_LED_MODE_SEL)); + pr_debug("RTL838X_LED_P_EN_CTRL: %x", sw_r32(RTL838X_LED_P_EN_CTRL)); + pr_debug("RTL838X_LED_MODE_CTRL: %x", sw_r32(RTL838X_LED_MODE_CTRL)); + + sw_w32_mask(3, 0, RTL838X_LED_MODE_SEL); + sw_w32(mode, RTL838X_LED_MODE_CTRL); + + /* Enable software control of all leds */ + sw_w32(0xFFFFFFF, RTL838X_LED_SW_CTRL); + sw_w32(0xFFFFFFF, RTL838X_LED_P_EN_CTRL); + + sw_w32(0x0000000, RTL838X_LED0_SW_P_EN_CTRL); + sw_w32(0x0000000, RTL838X_LED1_SW_P_EN_CTRL); + sw_w32(0x0000000, RTL838X_LED2_SW_P_EN_CTRL); + + sw_w32_mask(0x3f, 0, RTL838X_LED_GLB_CTRL); + switch (leds_per_port) { + case 3: + sw_w32_mask(0, 0x7 | (0x7 << 3), RTL838X_LED_GLB_CTRL); + sw_w32(0xFFFFFFF, RTL838X_LED2_SW_P_EN_CTRL); + /* FALLTHRU */ + case 2: + sw_w32_mask(0, 0x3 | (0x3 << 3), RTL838X_LED_GLB_CTRL); + sw_w32(0xFFFFFFF, RTL838X_LED1_SW_P_EN_CTRL); + /* FALLTHRU */ + case 1: + sw_w32_mask(0, 0x1 | (0x1 << 3), RTL838X_LED_GLB_CTRL); + sw_w32(0xFFFFFFF, RTL838X_LED0_SW_P_EN_CTRL); + break; + default: + pr_err("No LEDS configured for software control\n"); + } +} + +static const struct of_device_id rtl838x_gpio_of_match[] = { + { .compatible = "realtek,rtl838x-gpio" }, + {}, +}; + +MODULE_DEVICE_TABLE(of, rtl838x_gpio_of_match); + +static int rtl838x_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct rtl838x_gpios *gpios; + int err; + u8 indirect_bus_id; + + pr_info("Probing RTL838X GPIOs\n"); + + if (!np) { + dev_err(&pdev->dev, "No DT found\n"); + return -EINVAL; + } + + gpios = devm_kzalloc(dev, sizeof(*gpios), GFP_KERNEL); + if (!gpios) + return -ENOMEM; + + gpios->id = sw_r32(RTL838X_MODEL_NAME_INFO) >> 16; + + switch (gpios->id) { + case 0x8332: + pr_debug("Found RTL8332M GPIO\n"); + break; + case 0x8380: + pr_debug("Found RTL8380M GPIO\n"); + break; + case 0x8381: + pr_debug("Found RTL8381M GPIO\n"); + break; + case 0x8382: + pr_debug("Found RTL8382M GPIO\n"); + break; + default: + pr_err("Unknown GPIO chip id (%04x)\n", gpios->id); + return -ENODEV; + } + + gpios->dev = dev; + gpios->gc.base = 0; + /* 0-31: internal + * 32-63, LED control register + * 64-99: external RTL8231 + * 100-131: PORT-LED 0 + * 132-163: PORT-LED 1 + * 164-195: PORT-LED 2 + */ + gpios->gc.ngpio = 196; + gpios->gc.label = "rtl838x"; + gpios->gc.parent = dev; + gpios->gc.owner = THIS_MODULE; + gpios->gc.can_sleep = true; + gpios->bus_id = -1; + gpios->irq = 31; + + gpios->gc.direction_input = rtl838x_direction_input; + gpios->gc.direction_output = rtl838x_direction_output; + gpios->gc.set = rtl838x_gpio_set; + gpios->gc.get = rtl838x_gpio_get; + gpios->gc.get_direction = rtl838x_get_direction; + + if (!of_property_read_u8(np, "indirect-access-bus-id", &indirect_bus_id)) { + gpios->bus_id = indirect_bus_id; + rtl8231_init(gpios); + } + if (!of_property_read_u8(np, "smi-bus-id", &indirect_bus_id)) { + gpios->bus_id = indirect_bus_id; + gpios->smi_clock = RTL838X_GPIO_A2; + gpios->smi_data = RTL838X_GPIO_A3; + gpios->i2c_sda = 1; + gpios->i2c_sdc = 2; + rtl8380_rtl8321_init(gpios); + } + + if (of_property_read_bool(np, "take-port-leds")) { + if (of_property_read_u32(np, "leds-per-port", &gpios->leds_per_port)) + gpios->leds_per_port = 2; + if (of_property_read_u32(np, "led-mode", &gpios->led_mode)) + gpios->led_mode = (0x1ea << 15) | 0x1ea; + if (of_property_read_u32(np, "num-leds", &gpios->num_leds)) + gpios->num_leds = 32; + if (of_property_read_u32(np, "min-led", &gpios->min_led)) + gpios->min_led = 0; + take_port_leds(gpios); + } + + err = devm_gpiochip_add_data(dev, &gpios->gc, gpios); + return err; +} + + +static struct platform_driver rtl838x_gpio_driver = { + .driver = { + .name = "rtl838x-gpio", + .of_match_table = rtl838x_gpio_of_match, + }, + .probe = rtl838x_gpio_probe, +}; + +module_platform_driver(rtl838x_gpio_driver); + +MODULE_DESCRIPTION("Realtek RTL838X GPIO API support"); +MODULE_LICENSE("GPL v2"); diff --git a/target/linux/rtl838x/files-5.4/drivers/mtd/spi-nor/rtl838x-nor.c b/target/linux/rtl838x/files-5.4/drivers/mtd/spi-nor/rtl838x-nor.c new file mode 100644 index 0000000000..0d8f9dbd3e --- /dev/null +++ b/target/linux/rtl838x/files-5.4/drivers/mtd/spi-nor/rtl838x-nor.c @@ -0,0 +1,607 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rtl838x-spi.h" +#include + +extern struct rtl838x_soc_info soc_info; + +struct rtl838x_nor { + struct spi_nor nor; + struct device *dev; + volatile void __iomem *base; + bool fourByteMode; + u32 chipSize; + uint32_t flags; + uint32_t io_status; +}; + +static uint32_t spi_prep(struct rtl838x_nor *rtl838x_nor) +{ + /* Needed because of MMU constraints */ + SPI_WAIT_READY; + spi_w32w(SPI_CS_INIT, SFCSR); //deactivate CS0, CS1 + spi_w32w(0, SFCSR); //activate CS0,CS1 + spi_w32w(SPI_CS_INIT, SFCSR); //deactivate CS0, CS1 + + return (CS0 & rtl838x_nor->flags) ? (SPI_eCS0 & SPI_LEN_INIT) + : ((SPI_eCS1 & SPI_LEN_INIT) | SFCSR_CHIP_SEL); +} + +static uint32_t rtl838x_nor_get_SR(struct rtl838x_nor *rtl838x_nor) +{ + uint32_t sfcsr, sfdr; + + sfcsr = spi_prep(rtl838x_nor); + sfdr = (SPINOR_OP_RDSR)<<24; + + pr_debug("%s: rdid,sfcsr_val = %.8x,SFDR = %.8x\n", __func__, sfcsr, sfdr); + pr_debug("rdid,sfcsr = %.8x\n", sfcsr | SPI_LEN4); + spi_w32w(sfcsr, SFCSR); + spi_w32w(sfdr, SFDR); + spi_w32_mask(0, SPI_LEN4, SFCSR); + SPI_WAIT_READY; + + return spi_r32(SFDR); +} + +static void spi_write_disable(struct rtl838x_nor *rtl838x_nor) +{ + uint32_t sfcsr, sfdr; + + sfcsr = spi_prep(rtl838x_nor); + sfdr = (SPINOR_OP_WRDI) << 24; + spi_w32w(sfcsr, SFCSR); + spi_w32w(sfdr, SFDR); + pr_debug("%s: sfcsr_val = %.8x,SFDR = %.8x", __func__, sfcsr, sfdr); + + spi_prep(rtl838x_nor); +} + +static void spi_write_enable(struct rtl838x_nor *rtl838x_nor) +{ + uint32_t sfcsr, sfdr; + + sfcsr = spi_prep(rtl838x_nor); + sfdr = (SPINOR_OP_WREN) << 24; + spi_w32w(sfcsr, SFCSR); + spi_w32w(sfdr, SFDR); + pr_debug("%s: sfcsr_val = %.8x,SFDR = %.8x", __func__, sfcsr, sfdr); + + spi_prep(rtl838x_nor); +} + +static void spi_4b_set(struct rtl838x_nor *rtl838x_nor, bool enable) +{ + uint32_t sfcsr, sfdr; + + sfcsr = spi_prep(rtl838x_nor); + if (enable) + sfdr = (SPINOR_OP_EN4B) << 24; + else + sfdr = (SPINOR_OP_EX4B) << 24; + + spi_w32w(sfcsr, SFCSR); + spi_w32w(sfdr, SFDR); + pr_debug("%s: sfcsr_val = %.8x,SFDR = %.8x", __func__, sfcsr, sfdr); + + spi_prep(rtl838x_nor); +} + +static int rtl838x_get_addr_mode(struct rtl838x_nor *rtl838x_nor) +{ + int res = 3; + u32 reg; + + sw_w32(0x3, RTL838X_INT_RW_CTRL); + if (!sw_r32(RTL838X_EXT_VERSION)) { + if (sw_r32(RTL838X_STRAP_DBG) & (1 << 29)) + res = 4; + } else { + reg = sw_r32(RTL838X_PLL_CML_CTRL); + if ((reg & (1 << 30)) && (reg & (1 << 31))) + res = 4; + if ((!(reg & (1 << 30))) + && sw_r32(RTL838X_STRAP_DBG) & (1 << 29)) + res = 4; + } + sw_w32(0x0, RTL838X_INT_RW_CTRL); + return res; +} + +static int rtl8390_get_addr_mode(struct rtl838x_nor *rtl838x_nor) +{ + if (spi_r32(RTL8390_SOC_SPI_MMIO_CONF) & (1 << 9)) + return 4; + return 3; +} + +ssize_t rtl838x_do_read(struct rtl838x_nor *rtl838x_nor, loff_t from, + size_t length, u_char *buffer, uint8_t command) +{ + uint32_t sfcsr, sfdr; + uint32_t len = length; + + sfcsr = spi_prep(rtl838x_nor); + sfdr = command << 24; + + /* Perform SPINOR_OP_READ: 1 byte command & 3 byte addr*/ + sfcsr |= SPI_LEN4; + sfdr |= from; + + spi_w32w(sfcsr, SFCSR); + spi_w32w(sfdr, SFDR); + + /* Read Data, 4 bytes at a time */ + while (length >= 4) { + SPI_WAIT_READY; + *((uint32_t *) buffer) = spi_r32(SFDR); +/* printk("%.8x ", *((uint32_t*) buffer)); */ + buffer += 4; + length -= 4; + } + + /* The rest needs to be read 1 byte a time */ + sfcsr &= SPI_LEN_INIT|SPI_LEN1; + SPI_WAIT_READY; + spi_w32w(sfcsr, SFCSR); + while (length > 0) { + SPI_WAIT_READY; + *(buffer) = spi_r32(SFDR) >> 24; +/* printk("%.2x ", *(buffer)); */ + buffer++; + length--; + } + return len; +} + +/* + * Do fast read in 3 or 4 Byte addressing mode + */ +static ssize_t rtl838x_do_4bf_read(struct rtl838x_nor *rtl838x_nor, loff_t from, + size_t length, u_char *buffer, uint8_t command) +{ + int sfcsr_addr_len = rtl838x_nor->fourByteMode ? 0x3 : 0x2; + int sfdr_addr_shift = rtl838x_nor->fourByteMode ? 0 : 8; + uint32_t sfcsr; + uint32_t len = length; + + pr_debug("Fast read from %llx, len %x, shift %d\n", + from, sfcsr_addr_len, sfdr_addr_shift); + sfcsr = spi_prep(rtl838x_nor); + + /* Send read command */ + spi_w32w(sfcsr | SPI_LEN1, SFCSR); + spi_w32w(command << 24, SFDR); + + /* Send address */ + spi_w32w(sfcsr | (sfcsr_addr_len << 28), SFCSR); + spi_w32w(from << sfdr_addr_shift, SFDR); + + /* Dummy cycles */ + spi_w32w(sfcsr | SPI_LEN1, SFCSR); + spi_w32w(0, SFDR); + + /* Start reading */ + spi_w32w(sfcsr | SPI_LEN4, SFCSR); + + /* Read Data, 4 bytes at a time */ + while (length >= 4) { + SPI_WAIT_READY; + *((uint32_t *) buffer) = spi_r32(SFDR); +/* printk("%.8x ", *((uint32_t*) buffer)); */ + buffer += 4; + length -= 4; + } + + /* The rest needs to be read 1 byte a time */ + sfcsr &= SPI_LEN_INIT|SPI_LEN1; + SPI_WAIT_READY; + spi_w32w(sfcsr, SFCSR); + while (length > 0) { + SPI_WAIT_READY; + *(buffer) = spi_r32(SFDR) >> 24; +/* printk("%.2x ", *(buffer)); */ + buffer++; + length--; + } + return len; + +} + +/* + * Do write (Page Programming) in 3 or 4 Byte addressing mode + */ +static ssize_t rtl838x_do_4b_write(struct rtl838x_nor *rtl838x_nor, loff_t to, + size_t length, const u_char *buffer, + uint8_t command) +{ + int sfcsr_addr_len = rtl838x_nor->fourByteMode ? 0x3 : 0x2; + int sfdr_addr_shift = rtl838x_nor->fourByteMode ? 0 : 8; + uint32_t sfcsr; + uint32_t len = length; + + pr_debug("Write to %llx, len %x, shift %d\n", + to, sfcsr_addr_len, sfdr_addr_shift); + sfcsr = spi_prep(rtl838x_nor); + + /* Send write command, command IO-width is 1 (bit 25/26) */ + spi_w32w(sfcsr | SPI_LEN1 | (0 << 25), SFCSR); + spi_w32w(command << 24, SFDR); + + /* Send address */ + spi_w32w(sfcsr | (sfcsr_addr_len << 28) | (0 << 25), SFCSR); + spi_w32w(to << sfdr_addr_shift, SFDR); + + /* Write Data, 1 byte at a time, if we are not 4-byte aligned */ + if (((long)buffer) % 4) { + spi_w32w(sfcsr | SPI_LEN1, SFCSR); + while (length > 0 && (((long)buffer) % 4)) { + SPI_WAIT_READY; + spi_w32(*(buffer) << 24, SFDR); + buffer += 1; + length -= 1; + } + } + + /* Now we can write 4 bytes at a time */ + SPI_WAIT_READY; + spi_w32w(sfcsr | SPI_LEN4, SFCSR); + while (length >= 4) { + SPI_WAIT_READY; + spi_w32(*((uint32_t *)buffer), SFDR); + buffer += 4; + length -= 4; + } + + /* Final bytes might need to be written 1 byte at a time, again */ + SPI_WAIT_READY; + spi_w32w(sfcsr | SPI_LEN1, SFCSR); + while (length > 0) { + SPI_WAIT_READY; + spi_w32(*(buffer) << 24, SFDR); + buffer++; + length--; + } + return len; +} + +static ssize_t rtl838x_nor_write(struct spi_nor *nor, loff_t to, size_t len, + const u_char *buffer) +{ + int ret = 0; + uint32_t offset = 0; + struct rtl838x_nor *rtl838x_nor = nor->priv; + size_t l = len; + uint8_t cmd = SPINOR_OP_PP; + + /* Do write in 4-byte mode on large Macronix chips */ + if (rtl838x_nor->fourByteMode) { + cmd = SPINOR_OP_PP_4B; + spi_4b_set(rtl838x_nor, true); + } + + pr_debug("In %s %8x to: %llx\n", __func__, + (unsigned int) rtl838x_nor, to); + + while (l >= SPI_MAX_TRANSFER_SIZE) { + while + (rtl838x_nor_get_SR(rtl838x_nor) & SPI_WIP); + do { + spi_write_enable(rtl838x_nor); + } while (!(rtl838x_nor_get_SR(rtl838x_nor) & SPI_WEL)); + ret = rtl838x_do_4b_write(rtl838x_nor, to + offset, + SPI_MAX_TRANSFER_SIZE, buffer+offset, cmd); + l -= SPI_MAX_TRANSFER_SIZE; + offset += SPI_MAX_TRANSFER_SIZE; + } + + if (l > 0) { + while + (rtl838x_nor_get_SR(rtl838x_nor) & SPI_WIP); + do { + spi_write_enable(rtl838x_nor); + } while (!(rtl838x_nor_get_SR(rtl838x_nor) & SPI_WEL)); + ret = rtl838x_do_4b_write(rtl838x_nor, to+offset, + len, buffer+offset, cmd); + } + + return len; +} + +static ssize_t rtl838x_nor_read(struct spi_nor *nor, loff_t from, + size_t length, u_char *buffer) +{ + uint32_t offset = 0; + uint8_t cmd = SPINOR_OP_READ_FAST; + size_t l = length; + struct rtl838x_nor *rtl838x_nor = nor->priv; + + /* Do fast read in 3, or 4-byte mode on large Macronix chips */ + if (rtl838x_nor->fourByteMode) { + cmd = SPINOR_OP_READ_FAST_4B; + spi_4b_set(rtl838x_nor, true); + } + + /* TODO: do timeout and return error */ + pr_debug("Waiting for pending writes\n"); + while + (rtl838x_nor_get_SR(rtl838x_nor) & SPI_WIP); + do { + spi_write_enable(rtl838x_nor); + } while (!(rtl838x_nor_get_SR(rtl838x_nor) & SPI_WEL)); + + pr_debug("cmd is %d\n", cmd); + pr_debug("%s: addr %.8llx to addr %.8x, cmd %.8x, size %d\n", __func__, + from, (u32)buffer, (u32)cmd, length); + + while (l >= SPI_MAX_TRANSFER_SIZE) { + rtl838x_do_4bf_read(rtl838x_nor, from + offset, + SPI_MAX_TRANSFER_SIZE, buffer+offset, cmd); + l -= SPI_MAX_TRANSFER_SIZE; + offset += SPI_MAX_TRANSFER_SIZE; + } + + if (l > 0) + rtl838x_do_4bf_read(rtl838x_nor, from + offset, l, buffer+offset, cmd); + + return length; +} + +static int rtl838x_erase(struct spi_nor *nor, loff_t offs) +{ + struct rtl838x_nor *rtl838x_nor = nor->priv; + int sfcsr_addr_len = rtl838x_nor->fourByteMode ? 0x3 : 0x2; + int sfdr_addr_shift = rtl838x_nor->fourByteMode ? 0 : 8; + uint32_t sfcsr; + uint8_t cmd = SPINOR_OP_SE; + + pr_debug("Erasing sector at %llx\n", offs); + + /* Do erase in 4-byte mode on large Macronix chips */ + if (rtl838x_nor->fourByteMode) { + cmd = SPINOR_OP_SE_4B; + spi_4b_set(rtl838x_nor, true); + } + /* TODO: do timeout and return error */ + while + (rtl838x_nor_get_SR(rtl838x_nor) & SPI_WIP); + do { + spi_write_enable(rtl838x_nor); + } while (!(rtl838x_nor_get_SR(rtl838x_nor) & SPI_WEL)); + + sfcsr = spi_prep(rtl838x_nor); + + /* Send erase command, command IO-width is 1 (bit 25/26) */ + spi_w32w(sfcsr | SPI_LEN1 | (0 << 25), SFCSR); + spi_w32w(cmd << 24, SFDR); + + /* Send address */ + spi_w32w(sfcsr | (sfcsr_addr_len << 28) | (0 << 25), SFCSR); + spi_w32w(offs << sfdr_addr_shift, SFDR); + + return 0; +} + +static int rtl838x_nor_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) +{ + int length = len; + u8 *buffer = buf; + uint32_t sfcsr, sfdr; + struct rtl838x_nor *rtl838x_nor = nor->priv; + + pr_debug("In %s: opcode %x, len %x\n", __func__, opcode, len); + + sfcsr = spi_prep(rtl838x_nor); + sfdr = opcode << 24; + + sfcsr |= SPI_LEN1; + + spi_w32w(sfcsr, SFCSR); + spi_w32w(sfdr, SFDR); + + while (length > 0) { + SPI_WAIT_READY; + *(buffer) = spi_r32(SFDR) >> 24; + buffer++; + length--; + } + + return len; +} + +static int rtl838x_nor_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) +{ + uint32_t sfcsr, sfdr; + struct rtl838x_nor *rtl838x_nor = nor->priv; + + pr_debug("In %s, opcode %x, len %x\n", __func__, opcode, len); + sfcsr = spi_prep(rtl838x_nor); + sfdr = opcode << 24; + + if (len == 1) { /* SPINOR_OP_WRSR */ + sfdr |= buf[0]; + sfcsr |= SPI_LEN2; + } + spi_w32w(sfcsr, SFCSR); + spi_w32w(sfdr, SFDR); + return 0; +} + +static int spi_enter_sio(struct spi_nor *nor) +{ + uint32_t sfcsr, sfcr2, sfdr; + uint32_t ret = 0, reg = 0, size_bits; + struct rtl838x_nor *rtl838x_nor = nor->priv; + + pr_debug("In %s\n", __func__); + rtl838x_nor->io_status = 0; + sfdr = SPI_C_RSTQIO << 24; + sfcsr = spi_prep(rtl838x_nor); + + reg = spi_r32(SFCR2); + pr_debug("SFCR2: %x, size %x, rdopt: %x\n", reg, SFCR2_GETSIZE(reg), + (reg & SFCR2_RDOPT)); + size_bits = rtl838x_nor->fourByteMode ? SFCR2_SIZE(0x6) : SFCR2_SIZE(0x7); + + sfcr2 = SFCR2_HOLD_TILL_SFDR2 | size_bits + | (reg & SFCR2_RDOPT) | SFCR2_CMDIO(0) + | SFCR2_ADDRIO(0) | SFCR2_DUMMYCYCLE(4) + | SFCR2_DATAIO(0) | SFCR2_SFCMD(SPINOR_OP_READ_FAST); + pr_debug("SFCR2: %x, size %x\n", reg, SFCR2_GETSIZE(reg)); + + SPI_WAIT_READY; + spi_w32w(sfcr2, SFCR2); + spi_w32w(sfcsr, SFCSR); + spi_w32w(sfdr, SFDR); + + spi_w32_mask(SFCR2_HOLD_TILL_SFDR2, 0, SFCR2); + rtl838x_nor->io_status &= ~IOSTATUS_CIO_MASK; + rtl838x_nor->io_status |= CIO1; + + spi_prep(rtl838x_nor); + + return ret; +} + +int rtl838x_spi_nor_scan(struct spi_nor *nor, const char *name) +{ + static const struct spi_nor_hwcaps hwcaps = { + .mask = SNOR_HWCAPS_READ | SNOR_HWCAPS_PP + | SNOR_HWCAPS_READ_FAST + }; + + struct rtl838x_nor *rtl838x_nor = nor->priv; + + pr_debug("In %s\n", __func__); + + spi_w32_mask(0, SFCR_EnableWBO, SFCR); + spi_w32_mask(0, SFCR_EnableRBO, SFCR); + + rtl838x_nor->flags = CS0 | R_MODE; + + spi_nor_scan(nor, NULL, &hwcaps); + pr_debug("------------- Got size: %llx\n", nor->mtd.size); + + return 0; +} + +int rtl838x_nor_init(struct rtl838x_nor *rtl838x_nor, + struct device_node *flash_node) +{ + int ret; + struct spi_nor *nor; + + pr_info("%s called\n", __func__); + nor = &rtl838x_nor->nor; + nor->dev = rtl838x_nor->dev; + nor->priv = rtl838x_nor; + spi_nor_set_flash_node(nor, flash_node); + + nor->read_reg = rtl838x_nor_read_reg; + nor->write_reg = rtl838x_nor_write_reg; + nor->read = rtl838x_nor_read; + nor->write = rtl838x_nor_write; + nor->erase = rtl838x_erase; + nor->mtd.name = "rtl838x_nor"; + nor->erase_opcode = rtl838x_nor->fourByteMode ? SPINOR_OP_SE_4B + : SPINOR_OP_SE; + /* initialized with NULL */ + ret = rtl838x_spi_nor_scan(nor, NULL); + if (ret) + return ret; + + spi_enter_sio(nor); + spi_write_disable(rtl838x_nor); + + ret = mtd_device_parse_register(&nor->mtd, NULL, NULL, NULL, 0); + return ret; +} + +static int rtl838x_nor_drv_probe(struct platform_device *pdev) +{ + struct device_node *flash_np; + struct resource *res; + int ret; + struct rtl838x_nor *rtl838x_nor; + int addrMode; + + pr_info("Initializing rtl838x_nor_driver\n"); + if (!pdev->dev.of_node) { + dev_err(&pdev->dev, "No DT found\n"); + return -EINVAL; + } + + rtl838x_nor = devm_kzalloc(&pdev->dev, sizeof(*rtl838x_nor), GFP_KERNEL); + if (!rtl838x_nor) + return -ENOMEM; + platform_set_drvdata(pdev, rtl838x_nor); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + rtl838x_nor->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR((void *)rtl838x_nor->base)) + return PTR_ERR((void *)rtl838x_nor->base); + + pr_info("SPI resource base is %08x\n", (u32)rtl838x_nor->base); + rtl838x_nor->dev = &pdev->dev; + + /* only support one attached flash */ + flash_np = of_get_next_available_child(pdev->dev.of_node, NULL); + if (!flash_np) { + dev_err(&pdev->dev, "no SPI flash device to configure\n"); + ret = -ENODEV; + goto nor_free; + } + + /* Get the 3/4 byte address mode as configure by bootloader */ + if (soc_info.family == RTL8390_FAMILY_ID) + addrMode = rtl8390_get_addr_mode(rtl838x_nor); + else + addrMode = rtl838x_get_addr_mode(rtl838x_nor); + pr_info("Address mode is %d bytes\n", addrMode); + if (addrMode == 4) + rtl838x_nor->fourByteMode = true; + + ret = rtl838x_nor_init(rtl838x_nor, flash_np); + +nor_free: + return ret; +} + +static int rtl838x_nor_drv_remove(struct platform_device *pdev) +{ +/* struct rtl8xx_nor *rtl838x_nor = platform_get_drvdata(pdev); */ + return 0; +} + +static const struct of_device_id rtl838x_nor_of_ids[] = { + { .compatible = "realtek,rtl838x-nor"}, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rtl838x_nor_of_ids); + +static struct platform_driver rtl838x_nor_driver = { + .probe = rtl838x_nor_drv_probe, + .remove = rtl838x_nor_drv_remove, + .driver = { + .name = "rtl838x-nor", + .pm = NULL, + .of_match_table = rtl838x_nor_of_ids, + }, +}; + +module_platform_driver(rtl838x_nor_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("RTL838x SPI NOR Flash Driver"); diff --git a/target/linux/rtl838x/files-5.4/drivers/mtd/spi-nor/rtl838x-spi.h b/target/linux/rtl838x/files-5.4/drivers/mtd/spi-nor/rtl838x-spi.h new file mode 100644 index 0000000000..1209d47b02 --- /dev/null +++ b/target/linux/rtl838x/files-5.4/drivers/mtd/spi-nor/rtl838x-spi.h @@ -0,0 +1,111 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2009 Realtek Semiconductor Corp. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _RTL838X_SPI_H +#define _RTL838X_SPI_H + + +/* + * Register access macros + */ + +#define spi_r32(reg) __raw_readl(rtl838x_nor->base + reg) +#define spi_w32(val, reg) __raw_writel(val, rtl838x_nor->base + reg) +#define spi_w32_mask(clear, set, reg) \ + spi_w32((spi_r32(reg) & ~(clear)) | (set), reg) + +#define SPI_WAIT_READY do { \ + } while (!(spi_r32(SFCSR) & SFCSR_SPI_RDY)) + +#define spi_w32w(val, reg) do { \ + __raw_writel(val, rtl838x_nor->base + reg); \ + SPI_WAIT_READY; \ + } while (0) + +#define SFCR (0x00) /*SPI Flash Configuration Register*/ + #define SFCR_CLK_DIV(val) ((val)<<29) + #define SFCR_EnableRBO (1<<28) + #define SFCR_EnableWBO (1<<27) + #define SFCR_SPI_TCS(val) ((val)<<23) /*4 bit, 1111 */ + +#define SFCR2 (0x04) /*For memory mapped I/O */ + #define SFCR2_SFCMD(val) ((val)<<24) /*8 bit, 1111_1111 */ + #define SFCR2_SIZE(val) ((val)<<21) /*3 bit, 111 */ + #define SFCR2_RDOPT (1<<20) + #define SFCR2_CMDIO(val) ((val)<<18) /*2 bit, 11 */ + #define SFCR2_ADDRIO(val) ((val)<<16) /*2 bit, 11 */ + #define SFCR2_DUMMYCYCLE(val) ((val)<<13) /*3 bit, 111 */ + #define SFCR2_DATAIO(val) ((val)<<11) /*2 bit, 11 */ + #define SFCR2_HOLD_TILL_SFDR2 (1<<10) + #define SFCR2_GETSIZE(x) (((x)&0x00E00000)>>21) + +#define SFCSR (0x08) /*SPI Flash Control&Status Register*/ + #define SFCSR_SPI_CSB0 (1<<31) + #define SFCSR_SPI_CSB1 (1<<30) + #define SFCSR_LEN(val) ((val)<<28) /*2 bits*/ + #define SFCSR_SPI_RDY (1<<27) + #define SFCSR_IO_WIDTH(val) ((val)<<25) /*2 bits*/ + #define SFCSR_CHIP_SEL (1<<24) + #define SFCSR_CMD_BYTE(val) ((val)<<16) /*8 bit, 1111_1111 */ + +#define SFDR (0x0C) /*SPI Flash Data Register*/ +#define SFDR2 (0x10) /*SPI Flash Data Register - for post SPI bootup setting*/ + #define SPI_CS_INIT (SFCSR_SPI_CSB0 | SFCSR_SPI_CSB1 | SPI_LEN1) + #define SPI_CS0 SFCSR_SPI_CSB0 + #define SPI_CS1 SFCSR_SPI_CSB1 + #define SPI_eCS0 ((SFCSR_SPI_CSB1)) /*and SFCSR to active CS0*/ + #define SPI_eCS1 ((SFCSR_SPI_CSB0)) /*and SFCSR to active CS1*/ + + #define SPI_WIP (1) /* Write In Progress */ + #define SPI_WEL (1<<1) /* Write Enable Latch*/ + #define SPI_SST_QIO_WIP (1<<7) /* SST QIO Flash Write In Progress */ + #define SPI_LEN_INIT 0xCFFFFFFF /* and SFCSR to init */ + #define SPI_LEN4 0x30000000 /* or SFCSR to set */ + #define SPI_LEN3 0x20000000 /* or SFCSR to set */ + #define SPI_LEN2 0x10000000 /* or SFCSR to set */ + #define SPI_LEN1 0x00000000 /* or SFCSR to set */ + #define SPI_SETLEN(val) do { \ + SPI_REG(SFCSR) &= 0xCFFFFFFF; \ + SPI_REG(SFCSR) |= (val-1)<<28; \ + } while (0) +/* + * SPI interface control + */ +#define RTL8390_SOC_SPI_MMIO_CONF (0x04) + +#define IOSTATUS_CIO_MASK (0x00000038) + +/* Chip select: bits 4-7*/ +#define CS0 (1<<4) +#define R_MODE 0x04 + +/* io_status */ +#define IO1 (1<<0) +#define IO2 (1<<1) +#define CIO1 (1<<3) +#define CIO2 (1<<4) +#define CMD_IO1 (1<<6) +#define W_ADDR_IO1 ((1)<<12) +#define R_ADDR_IO2 ((2)<<9) +#define R_DATA_IO2 ((2)<<15) +#define W_DATA_IO1 ((1)<<18) + +/* Commands */ +#define SPI_C_RSTQIO 0xFF + +#define SPI_MAX_TRANSFER_SIZE 256 + +#endif /* _RTL838X_SPI_H */ diff --git a/target/linux/rtl838x/files-5.4/drivers/net/dsa/rtl838x.h b/target/linux/rtl838x/files-5.4/drivers/net/dsa/rtl838x.h new file mode 100644 index 0000000000..127a31dca1 --- /dev/null +++ b/target/linux/rtl838x/files-5.4/drivers/net/dsa/rtl838x.h @@ -0,0 +1,188 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _RTL838X_H +#define _RTL838X_H + +#include + +/* + * Register definition + */ +#define RTL838X_CPU_PORT 28 +#define RTL839X_CPU_PORT 52 + +#define RTL838X_MAC_PORT_CTRL(port) (0xd560 + (((port) << 7))) +#define RTL839X_MAC_PORT_CTRL(port) (0x8004 + (((port) << 7))) +#define RTL838X_RST_GLB_CTRL_0 (0x003c) +#define RTL838X_MAC_FORCE_MODE_CTRL (0xa104) +#define RTL839X_MAC_FORCE_MODE_CTRL (0x02bc) + +#define RTL838X_DMY_REG31 (0x3b28) +#define RTL838X_SDS_MODE_SEL (0x0028) +#define RTL838X_SDS_CFG_REG (0x0034) +#define RTL838X_INT_MODE_CTRL (0x005c) +#define RTL838X_CHIP_INFO (0x00d8) +#define RTL839X_CHIP_INFO (0x0ff4) +#define RTL838X_SDS4_REG28 (0xef80) +#define RTL838X_SDS4_DUMMY0 (0xef8c) +#define RTL838X_SDS5_EXT_REG6 (0xf18c) +#define RTL838X_PORT_ISO_CTRL(port) (0x4100 + ((port) << 2)) +#define RTL839X_PORT_ISO_CTRL(port) (0x1400 + ((port) << 3)) +#define RTL8380_SDS4_FIB_REG0 (0xF800) +#define RTL838X_STAT_PORT_STD_MIB (0x1200) +#define RTL839X_STAT_PORT_STD_MIB (0xC000) +#define RTL838X_STAT_RST (0x3100) +#define RTL839X_STAT_RST (0xF504) +#define RTL838X_STAT_PORT_RST (0x3104) +#define RTL839X_STAT_PORT_RST (0xF508) +#define RTL838X_STAT_CTRL (0x3108) +#define RTL839X_STAT_CTRL (0x04cc) + +/* Registers of the internal Serdes of the 8380 */ +#define MAPLE_SDS4_REG0r RTL838X_SDS4_REG28 +#define MAPLE_SDS5_REG0r (RTL838X_SDS4_REG28 + 0x100) +#define MAPLE_SDS4_REG3r RTL838X_SDS4_DUMMY0 +#define MAPLE_SDS5_REG3r (RTL838X_SDS4_REG28 + 0x100) +#define MAPLE_SDS4_FIB_REG0r (RTL838X_SDS4_REG28 + 0x880) +#define MAPLE_SDS5_FIB_REG0r (RTL838X_SDS4_REG28 + 0x980) + +/* VLAN registers */ +#define RTL838X_VLAN_PROFILE(idx) (0x3A88 + ((idx) << 2)) +#define RTL838X_VLAN_PORT_EGR_FLTR (0x3A84) +#define RTL838X_VLAN_PORT_PB_VLAN(port) (0x3C00 + ((port) << 2)) +#define RTL838X_VLAN_PORT_IGR_FLTR_0 (0x3A7C) +#define RTL838X_VLAN_PORT_IGR_FLTR_1 (0x3A7C + 4) + +/* Table 0/1 access registers */ +#define RTL838X_TBL_ACCESS_CTRL_0 (0x6914) +#define RTL838X_TBL_ACCESS_DATA_0(idx) (0x6918 + ((idx) << 2)) +#define RTL838X_TBL_ACCESS_CTRL_1 (0xA4C8) +#define RTL838X_TBL_ACCESS_DATA_1(idx) (0xA4CC + ((idx) << 2)) +#define RTL839X_TBL_ACCESS_CTRL_0 (0x1190) +#define RTL839X_TBL_ACCESS_DATA_0(idx) (0x1194 + ((idx) << 2)) +#define RTL839X_TBL_ACCESS_CTRL_1 (0x6b80) +#define RTL839X_TBL_ACCESS_DATA_1(idx) (0x6b84 + ((idx) << 2)) + +/* MAC handling */ +#define RTL838X_MAC_LINK_STS (0xa188) +#define RTL839X_MAC_LINK_STS (0x0390) +#define RTL838X_MAC_LINK_SPD_STS(port) (0xa190 + (((port >> 4) << 2))) +#define RTL838X_MAC_LINK_DUP_STS (0xa19c) +#define RTL838X_MAC_TX_PAUSE_STS (0xa1a0) +#define RTL838X_MAC_RX_PAUSE_STS (0xa1a4) +#define RTL838X_EEE_TX_TIMER_GIGA_CTRL (0xaa04) +#define RTL838X_EEE_TX_TIMER_GELITE_CTRL (0xaa08) + +/* MAC link state bits */ +#define FORCE_EN (1 << 0) +#define FORCE_LINK_EN (1 << 1) +#define NWAY_EN (1 << 2) +#define DUPLX_MODE (1 << 3) +#define TX_PAUSE_EN (1 << 6) +#define RX_PAUSE_EN (1 << 7) + +/* EEE */ +#define RTL838X_MAC_EEE_ABLTY (0xa1a8) +#define RTL838X_EEE_PORT_TX_EN (0x014c) +#define RTL838X_EEE_PORT_RX_EN (0x0150) +#define RTL838X_EEE_CLK_STOP_CTRL (0x0148) + +/* L2 functionality */ +#define RTL838X_L2_CTRL_0 (0x3200) +#define RTL839X_L2_CTRL_0 (0x3800) +#define RTL838X_L2_CTRL_1 (0x3204) +#define RTL839X_L2_CTRL_1 (0x3804) +#define RTL838X_L2_PORT_AGING_OUT (0x3358) +#define RTL839X_L2_PORT_AGING_OUT (0x3b74) +#define RTL838X_TBL_ACCESS_L2_CTRL (0x6900) +#define RTL839X_TBL_ACCESS_L2_CTRL (0x1180) +#define RTL838X_TBL_ACCESS_L2_DATA(idx) (0x6908 + ((idx) << 2)) +#define RTL838X_TBL_ACCESS_L2_DATA(idx) (0x6908 + ((idx) << 2)) +#define RTL838X_L2_TBL_FLUSH_CTRL (0x3370) +#define RTL839X_L2_TBL_FLUSH_CTRL (0x3ba0) + +/* Port Mirroring */ +#define RTL838X_MIR_CTRL(grp) (0x5D00 + (((grp) << 2))) +#define RTL838X_MIR_DPM_CTRL(grp) (0x5D20 + (((grp) << 2))) +#define RTL838X_MIR_SPM_CTRL(grp) (0x5D10 + (((grp) << 2))) + +enum phy_type { + PHY_NONE = 0, + PHY_RTL838X_SDS = 1, + PHY_RTL8218B_INT = 2, + PHY_RTL8218B_EXT = 3, + PHY_RTL8214FC = 4 +}; + +struct rtl838x_port { + bool enable; + u64 pm; + u16 pvid; + bool eee_enabled; + enum phy_type phy; +}; + +struct rtl838x_vlan_info { + u64 untagged_ports; + u64 tagged_ports; + u32 vlan_conf; +}; + +struct rtl838x_switch_priv; + +struct rtl838x_reg { + void (*mask_port_reg)(u64 clear, u64 set, int reg); + void (*set_port_reg)(u64 set, int reg); + u64 (*get_port_reg)(int reg); + int stat_port_rst; + int stat_rst; + int (*stat_port_std_mib)(int p); + void (*mask_port_iso_ctrl)(u64 clear, u64 set, int port); + void (*set_port_iso_ctrl)(u64 set, int port); + int l2_ctrl_0; + int l2_ctrl_1; + int l2_port_aging_out; + int smi_poll_ctrl; + int l2_tbl_flush_ctrl; + void (*exec_tbl0_cmd)(u32 cmd); + void (*exec_tbl1_cmd)(u32 cmd); + int (*tbl_access_data_0)(int i); + int isr_glb_src; + int isr_port_link_sts_chg; + int imr_port_link_sts_chg; + int imr_glb; + void (*vlan_tables_read)(u32 vlan, struct rtl838x_vlan_info *info); + void (*vlan_set_tagged)(u32 vlan, u64 portmask, u32 conf); + void (*vlan_set_untagged)(u32 vlan, u64 portmask); + int (*mac_force_mode_ctrl)(int port); + int rst_glb_ctrl; +}; + +struct rtl838x_switch_priv { + /* Switch operation */ + struct dsa_switch *ds; + struct device *dev; + u16 id; + u16 family_id; + char version; + struct rtl838x_port ports[54]; /* TODO: correct size! */ + struct mutex reg_mutex; + int link_state_irq; + int mirror_group_ports[4]; + struct mii_bus *mii_bus; + const struct rtl838x_reg *r; + u8 cpu_port; + u8 port_mask; +}; + +extern struct rtl838x_soc_info soc_info; +extern void rtl8380_sds_rst(int mac); + +extern int rtl838x_write_phy(u32 port, u32 page, u32 reg, u32 val); +extern int rtl839x_write_phy(u32 port, u32 page, u32 reg, u32 val); +extern int rtl838x_read_phy(u32 port, u32 page, u32 reg, u32 *val); +extern int rtl839x_read_phy(u32 port, u32 page, u32 reg, u32 *val); +extern int rtl838x_write_mmd_phy(u32 port, u32 addr, u32 reg, u32 val); +extern int rtl838x_read_mmd_phy(u32 port, u32 addr, u32 reg, u32 *val); + +#endif /* _RTL838X_H */ diff --git a/target/linux/rtl838x/files-5.4/drivers/net/dsa/rtl838x_phy.c b/target/linux/rtl838x/files-5.4/drivers/net/dsa/rtl838x_phy.c new file mode 100644 index 0000000000..4440dc99cd --- /dev/null +++ b/target/linux/rtl838x/files-5.4/drivers/net/dsa/rtl838x_phy.c @@ -0,0 +1,1286 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Realtek RTL838X Ethernet MDIO interface driver + * + * Copyright (C) 2020 B. Koblitz + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "rtl838x.h" + +/* External RTL8218B and RTL8214FC IDs are identical */ +#define PHY_ID_RTL8214C 0x001cc942 +#define PHY_ID_RTL8214FC 0x001cc981 +#define PHY_ID_RTL8218B_E 0x001cc981 +#define PHY_ID_RTL8218B_I 0x001cca40 +#define PHY_ID_RTL8390_GENERIC 0x001ccab0 + +struct __attribute__ ((__packed__)) part { + uint16_t start; + uint8_t wordsize; + uint8_t words; +}; + +struct __attribute__ ((__packed__)) fw_header { + uint32_t magic; + uint32_t phy; + uint32_t checksum; + uint32_t version; + struct part parts[10]; +}; + +#define FIRMWARE_838X_8380_1 "rtl838x_phy/rtl838x_8380.fw" +#define FIRMWARE_838X_8214FC_1 "rtl838x_phy/rtl838x_8214fc.fw" +#define FIRMWARE_838X_8218b_1 "rtl838x_phy/rtl838x_8218b.fw" + +static const struct firmware rtl838x_8380_fw; +static const struct firmware rtl838x_8214fc_fw; +static const struct firmware rtl838x_8218b_fw; + +struct rtl838x_phy_priv { + char *name; +}; + +static int read_phy(u32 port, u32 page, u32 reg, u32 *val) +{ + if (soc_info.family == RTL8390_FAMILY_ID) + return rtl839x_read_phy(port, page, reg, val); + else + return rtl838x_read_phy(port, page, reg, val); +} + +static int write_phy(u32 port, u32 page, u32 reg, u32 val) +{ + if (soc_info.family == RTL8390_FAMILY_ID) + return rtl839x_write_phy(port, page, reg, val); + else + return rtl838x_write_phy(port, page, reg, val); +} + +static void rtl8380_int_phy_on_off(int mac, bool on) +{ + u32 val; + + read_phy(mac, 0, 0, &val); + if (on) + write_phy(mac, 0, 0, val & ~(1 << 11)); + else + write_phy(mac, 0, 0, val | (1 << 11)); +} + +static void rtl8380_rtl8214fc_on_off(int mac, bool on) +{ + u32 val; + + /* fiber ports */ + write_phy(mac, 4095, 30, 3); + read_phy(mac, 0, 16, &val); + if (on) + write_phy(mac, 0, 16, val & ~(1 << 11)); + else + write_phy(mac, 0, 16, val | (1 << 11)); + + /* copper ports */ + write_phy(mac, 4095, 30, 1); + read_phy(mac, 0, 16, &val); + if (on) + write_phy(mac, 0xa40, 16, val & ~(1 << 11)); + else + write_phy(mac, 0xa40, 16, val | (1 << 11)); +} + +static void rtl8380_phy_reset(int mac) +{ + u32 val; + + read_phy(mac, 0, 0, &val); + write_phy(mac, 0, 0, val | (0x1 << 15)); +} + +void rtl8380_sds_rst(int mac) +{ + u32 offset = (mac == 24) ? 0 : 0x100; + + sw_w32_mask(1 << 11, 0, RTL8380_SDS4_FIB_REG0 + offset); + sw_w32_mask(0x3, 0, RTL838X_SDS4_REG28 + offset); + sw_w32_mask(0x3, 0x3, RTL838X_SDS4_REG28 + offset); + sw_w32_mask(0, 0x1 << 6, RTL838X_SDS4_DUMMY0 + offset); + sw_w32_mask(0x1 << 6, 0, RTL838X_SDS4_DUMMY0 + offset); + pr_info("SERDES reset: %d\n", mac); +} + +static struct fw_header * +rtl838x_request_fw(struct phy_device *phydev, const struct firmware *fw, + const char *name) +{ + struct device *dev = &phydev->mdio.dev; + int err; + struct fw_header *h; + uint32_t checksum, my_checksum; + + err = request_firmware(&fw, name, dev); + if (err < 0) + goto out; + + if (fw->size < sizeof(struct fw_header)) { + pr_err("Firmware size too small.\n"); + err = -EINVAL; + goto out; + } + + h = (struct fw_header *) fw->data; + pr_info("Firmware loaded. Size %d, magic: %08x\n", fw->size, h->magic); + + if (h->phy != 0x83800000) { + pr_err("Wrong firmware file: PHY mismatch.\n"); + goto out; + } + + checksum = h->checksum; + h->checksum = 0; + my_checksum = ~crc32(0xFFFFFFFFU, fw->data, fw->size); + if (checksum != my_checksum) { + pr_err("Firmware checksum mismatch.\n"); + err = -EINVAL; + goto out; + } + h->checksum = checksum; + + return h; +out: + dev_err(dev, "Unable to load firmware %s (%d)\n", name, err); + return NULL; +} + +static int rtl8390_configure_generic(struct phy_device *phydev) +{ + u32 val, phy_id; + int mac = phydev->mdio.addr; + + read_phy(mac, 0, 2, &val); + phy_id = val << 16; + read_phy(mac, 0, 3, &val); + phy_id |= val; + pr_debug("Phy on MAC %d: %x\n", mac, phy_id); + + /* Read internal PHY ID */ + write_phy(mac, 31, 27, 0x0002); + read_phy(mac, 31, 28, &val); + + /* Internal RTL8218B, version 2 */ + phydev_info(phydev, "Detected unknown %x\n", val); + return 0; +} + +static int rtl8380_configure_int_rtl8218b(struct phy_device *phydev) +{ + u32 val, phy_id; + int i, p, ipd_flag; + int mac = phydev->mdio.addr; + struct fw_header *h; + u32 *rtl838x_6275B_intPhy_perport; + u32 *rtl8218b_6276B_hwEsd_perport; + + + read_phy(mac, 0, 2, &val); + phy_id = val << 16; + read_phy(mac, 0, 3, &val); + phy_id |= val; + pr_debug("Phy on MAC %d: %x\n", mac, phy_id); + + /* Read internal PHY ID */ + write_phy(mac, 31, 27, 0x0002); + read_phy(mac, 31, 28, &val); + if (val != 0x6275) { + phydev_err(phydev, "Expected internal RTL8218B, found PHY-ID %x\n", val); + return -1; + } + + /* Internal RTL8218B, version 2 */ + phydev_info(phydev, "Detected internal RTL8218B\n"); + + h = rtl838x_request_fw(phydev, &rtl838x_8380_fw, FIRMWARE_838X_8380_1); + if (!h) + return -1; + + if (h->phy != 0x83800000) { + phydev_err(phydev, "Wrong firmware file: PHY mismatch.\n"); + return -1; + } + + rtl838x_6275B_intPhy_perport = (void *)h + sizeof(struct fw_header) + + h->parts[8].start; + + rtl8218b_6276B_hwEsd_perport = (void *)h + sizeof(struct fw_header) + + h->parts[9].start; + + if (sw_r32(RTL838X_DMY_REG31) == 0x1) + ipd_flag = 1; + + read_phy(mac, 0, 0, &val); + if (val & (1 << 11)) + rtl8380_int_phy_on_off(mac, true); + else + rtl8380_phy_reset(mac); + msleep(100); + + /* Ready PHY for patch */ + for (p = 0; p < 8; p++) { + write_phy(mac + p, 0xfff, 0x1f, 0x0b82); + write_phy(mac + p, 0xfff, 0x10, 0x0010); + } + msleep(500); + for (p = 0; p < 8; p++) { + for (i = 0; i < 100 ; i++) { + read_phy(mac + p, 0x0b80, 0x10, &val); + if (val & 0x40) + break; + } + if (i >= 100) { + phydev_err(phydev, + "ERROR: Port %d not ready for patch.\n", + mac + p); + return -1; + } + } + for (p = 0; p < 8; p++) { + i = 0; + while (rtl838x_6275B_intPhy_perport[i * 2]) { + write_phy(mac + p, 0xfff, + rtl838x_6275B_intPhy_perport[i * 2], + rtl838x_6275B_intPhy_perport[i * 2 + 1]); + i++; + } + i = 0; + while (rtl8218b_6276B_hwEsd_perport[i * 2]) { + write_phy(mac + p, 0xfff, + rtl8218b_6276B_hwEsd_perport[i * 2], + rtl8218b_6276B_hwEsd_perport[i * 2 + 1]); + i++; + } + } + return 0; +} + +static int rtl8380_configure_ext_rtl8218b(struct phy_device *phydev) +{ + u32 val, ipd, phy_id; + int i, l; + int mac = phydev->mdio.addr; + struct fw_header *h; + u32 *rtl8380_rtl8218b_perchip; + u32 *rtl8218B_6276B_rtl8380_perport; + u32 *rtl8380_rtl8218b_perport; + + if (soc_info.family == RTL8380_FAMILY_ID && mac != 0 && mac != 16) { + phydev_err(phydev, "External RTL8218B must have PHY-IDs 0 or 16!\n"); + return -1; + } + read_phy(mac, 0, 2, &val); + phy_id = val << 16; + read_phy(mac, 0, 3, &val); + phy_id |= val; + pr_info("Phy on MAC %d: %x\n", mac, phy_id); + + /* Read internal PHY ID */ + write_phy(mac, 31, 27, 0x0002); + read_phy(mac, 31, 28, &val); + if (val != 0x6276) { + phydev_err(phydev, "Expected external RTL8218B, found PHY-ID %x\n", val); + return -1; + } + phydev_info(phydev, "Detected external RTL8218B\n"); + + h = rtl838x_request_fw(phydev, &rtl838x_8218b_fw, FIRMWARE_838X_8218b_1); + if (!h) + return -1; + + if (h->phy != 0x8218b00) { + phydev_err(phydev, "Wrong firmware file: PHY mismatch.\n"); + return -1; + } + + rtl8380_rtl8218b_perchip = (void *)h + sizeof(struct fw_header) + + h->parts[0].start; + + rtl8218B_6276B_rtl8380_perport = (void *)h + sizeof(struct fw_header) + + h->parts[1].start; + + rtl8380_rtl8218b_perport = (void *)h + sizeof(struct fw_header) + + h->parts[2].start; + + read_phy(mac, 0, 0, &val); + if (val & (1 << 11)) + rtl8380_int_phy_on_off(mac, true); + else + rtl8380_phy_reset(mac); + msleep(100); + + /* Get Chip revision */ + write_phy(mac, 0xfff, 0x1f, 0x0); + write_phy(mac, 0xfff, 0x1b, 0x4); + read_phy(mac, 0xfff, 0x1c, &val); + + i = 0; + while (rtl8380_rtl8218b_perchip[i * 3] + && rtl8380_rtl8218b_perchip[i * 3 + 1]) { + write_phy(mac + rtl8380_rtl8218b_perchip[i * 3], + 0xfff, rtl8380_rtl8218b_perchip[i * 3 + 1], + rtl8380_rtl8218b_perchip[i * 3 + 2]); + i++; + } + + /* Enable PHY */ + for (i = 0; i < 8; i++) { + write_phy(mac + i, 0xfff, 0x1f, 0x0000); + write_phy(mac + i, 0xfff, 0x00, 0x1140); + } + mdelay(100); + + /* Request patch */ + for (i = 0; i < 8; i++) { + write_phy(mac + i, 0xfff, 0x1f, 0x0b82); + write_phy(mac + i, 0xfff, 0x10, 0x0010); + } + mdelay(300); + + /* Verify patch readiness */ + for (i = 0; i < 8; i++) { + for (l = 0; l < 100; l++) { + read_phy(mac + i, 0xb80, 0x10, &val); + if (val & 0x40) + break; + } + if (l >= 100) { + phydev_err(phydev, "Could not patch PHY\n"); + return -1; + } + } + + /* Use Broadcast ID method for patching */ + write_phy(mac, 0xfff, 0x1f, 0x0000); + write_phy(mac, 0xfff, 0x1d, 0x0008); + write_phy(mac, 0xfff, 0x1f, 0x0266); + write_phy(mac, 0xfff, 0x16, 0xff00 + mac); + write_phy(mac, 0xfff, 0x1f, 0x0000); + write_phy(mac, 0xfff, 0x1d, 0x0000); + mdelay(1); + + write_phy(mac, 0xfff, 30, 8); + write_phy(mac, 0x26e, 17, 0xb); + write_phy(mac, 0x26e, 16, 0x2); + mdelay(1); + read_phy(mac, 0x26e, 19, &ipd); + write_phy(mac, 0, 30, 0); + ipd = (ipd >> 4) & 0xf; + + i = 0; + while (rtl8218B_6276B_rtl8380_perport[i * 2]) { + write_phy(mac, 0xfff, rtl8218B_6276B_rtl8380_perport[i * 2], + rtl8218B_6276B_rtl8380_perport[i * 2 + 1]); + i++; + } + + /*Disable broadcast ID*/ + write_phy(mac, 0xfff, 0x1f, 0x0000); + write_phy(mac, 0xfff, 0x1d, 0x0008); + write_phy(mac, 0xfff, 0x1f, 0x0266); + write_phy(mac, 0xfff, 0x16, 0x00 + mac); + write_phy(mac, 0xfff, 0x1f, 0x0000); + write_phy(mac, 0xfff, 0x1d, 0x0000); + mdelay(1); + + return 0; +} + +static int rtl8218b_ext_match_phy_device(struct phy_device *phydev) +{ + int addr = phydev->mdio.addr; + + return phydev->phy_id == PHY_ID_RTL8218B_E && addr < 8; +} + + +static int rtl8380_rtl8218b_write_mmd(struct phy_device *phydev, + int devnum, u16 regnum, u16 val) +{ + int addr = phydev->mdio.addr; + + return rtl838x_write_mmd_phy(addr, devnum, regnum, val); +} + +static int rtl8380_rtl8218b_read_mmd(struct phy_device *phydev, + int devnum, u16 regnum) +{ + int ret; + u32 val; + int addr = phydev->mdio.addr; + + ret = rtl838x_read_mmd_phy(addr, devnum, regnum, &val); + if (ret) + return ret; + return val; +} + +static void rtl8380_rtl8214fc_media_set(int mac, bool set_fibre) +{ + int base = mac - (mac % 4); + static int reg[] = {16, 19, 20, 21}; + int val, media, power; + + pr_info("%s: port %d, set_fibre: %d\n", __func__, mac, set_fibre); + write_phy(base, 0xfff, 29, 8); + read_phy(base, 0x266, reg[mac % 4], &val); + + media = (val >> 10) & 0x3; + pr_info("Current media %x\n", media); + if (media & 0x2) { + pr_info("Powering off COPPER\n"); + write_phy(base, 0xfff, 29, 1); + /* Ensure power is off */ + read_phy(base, 0xa40, 16, &power); + if (!(power & (1 << 11))) + write_phy(base, 0xa40, 16, power | (1 << 11)); + } else { + pr_info("Powering off FIBRE"); + write_phy(base, 0xfff, 29, 3); + /* Ensure power is off */ + read_phy(base, 0xa40, 16, &power); + if (!(power & (1 << 11))) + write_phy(base, 0xa40, 16, power | (1 << 11)); + } + + if (set_fibre) { + val |= 1 << 10; + val &= ~(1 << 11); + } else { + val |= 1 << 10; + val |= 1 << 11; + } + write_phy(base, 0xfff, 29, 8); + write_phy(base, 0x266, reg[mac % 4], val); + write_phy(base, 0xfff, 29, 0); + + if (set_fibre) { + pr_info("Powering on FIBRE"); + write_phy(base, 0xfff, 29, 3); + /* Ensure power is off */ + read_phy(base, 0xa40, 16, &power); + if (power & (1 << 11)) + write_phy(base, 0xa40, 16, power & ~(1 << 11)); + } else { + pr_info("Powering on COPPER\n"); + write_phy(base, 0xfff, 29, 1); + /* Ensure power is off */ + read_phy(base, 0xa40, 16, &power); + if (power & (1 << 11)) + write_phy(base, 0xa40, 16, power & ~(1 << 11)); + } + + write_phy(base, 0xfff, 29, 0); +} + +static bool rtl8380_rtl8214fc_media_is_fibre(int mac) +{ + int base = mac - (mac % 4); + static int reg[] = {16, 19, 20, 21}; + u32 val; + + write_phy(base, 0xfff, 29, 8); + read_phy(base, 0x266, reg[mac % 4], &val); + write_phy(base, 0xfff, 29, 0); + if (val & (1 << 11)) + return false; + return true; +} + +static int rtl8380_rtl8214fc_set_port(struct phy_device *phydev, int port) +{ + bool is_fibre = (port == PORT_FIBRE ? true : false); + int addr = phydev->mdio.addr; + + pr_debug("%s port %d to %d\n", __func__, addr, port); + + rtl8380_rtl8214fc_media_set(addr, is_fibre); + return 0; +} + +static int rtl8380_rtl8214fc_get_port(struct phy_device *phydev) +{ + int addr = phydev->mdio.addr; + + pr_debug("%s: port %d\n", __func__, addr); + if (rtl8380_rtl8214fc_media_is_fibre(addr)) + return PORT_FIBRE; + return PORT_MII; +} + +void rtl8380_rtl8214fc_ldps_set(int mac, struct ethtool_eee *e) +{ + +} + +static void rtl8380_rtl8218b_eee_set_u_boot(int port, bool enable) +{ + u32 val; + bool an_enabled; + + /* Set GPHY page to copper */ + write_phy(port, 0, 30, 0x0001); + read_phy(port, 0, 0, &val); + an_enabled = val & (1 << 12); + + if (enable) { + /* 100/1000M EEE Capability */ + write_phy(port, 0, 13, 0x0007); + write_phy(port, 0, 14, 0x003C); + write_phy(port, 0, 13, 0x4007); + write_phy(port, 0, 14, 0x0006); + + read_phy(port, 0x0A43, 25, &val); + val |= 1 << 4; + write_phy(port, 0x0A43, 25, val); + } else { + /* 100/1000M EEE Capability */ + write_phy(port, 0, 13, 0x0007); + write_phy(port, 0, 14, 0x003C); + write_phy(port, 0, 13, 0x0007); + write_phy(port, 0, 14, 0x0000); + + read_phy(port, 0x0A43, 25, &val); + val &= ~(1 << 4); + write_phy(port, 0x0A43, 25, val); + } + + /* Restart AN if enabled */ + if (an_enabled) { + read_phy(port, 0, 0, &val); + val |= (1 << 12) | (1 << 9); + write_phy(port, 0, 0, val); + } + + /* GPHY page back to auto*/ + write_phy(port, 0xa42, 29, 0); +} + +static int rtl8380_rtl8218b_get_eee_u_boot(struct phy_device *phydev, struct ethtool_eee *e) +{ + u32 val; + int addr = phydev->mdio.addr; + + pr_debug("In %s %d\n", __func__, addr); + + /* Set GPHY page to copper */ + write_phy(addr, 0xa42, 29, 0x0001); + + read_phy(addr, 0xa43, 25, &val); + if (e->eee_enabled && (!!(val & (1 << 4)))) + e->eee_enabled = !!(val & (1 << 4)); + else + e->eee_enabled = 0; + + /* GPHY page to auto */ + write_phy(addr, 0xa42, 29, 0x0000); + + return 0; +} + +void rtl8380_rtl8218b_eee_set(int port, bool enable) +{ + u32 val; + bool an_enabled; + + pr_debug("In %s %d, enable %d\n", __func__, port, enable); + /* Set GPHY page to copper */ + write_phy(port, 0xa42, 29, 0x0001); + + read_phy(port, 0, 0, &val); + an_enabled = val & (1 << 12); + + /* MAC based EEE */ + read_phy(port, 0xa43, 25, &val); + val &= ~(1 << 5); + write_phy(port, 0xa43, 25, val); + + /* 100M / 1000M EEE */ + if (enable) + rtl838x_write_mmd_phy(port, 7, 60, 0x6); + else + rtl838x_write_mmd_phy(port, 7, 60, 0); + + /* 500M EEE ability */ + read_phy(port, 0xa42, 20, &val); + if (enable) + val |= 1 << 7; + else + val &= ~(1 << 7); + write_phy(port, 0xa42, 20, val); + + /* Restart AN if enabled */ + if (an_enabled) { + read_phy(port, 0, 0, &val); + val |= (1 << 12) | (1 << 9); + write_phy(port, 0, 0, val); + } + + /* GPHY page back to auto*/ + write_phy(port, 0xa42, 29, 0); +} + +int rtl8380_rtl8218b_get_eee(struct phy_device *phydev, + struct ethtool_eee *e) +{ + u32 val; + int addr = phydev->mdio.addr; + + pr_debug("In %s, port %d\n", __func__, addr); + + /* Set GPHY page to copper */ + write_phy(addr, 0xa42, 29, 0x0001); + + rtl838x_read_mmd_phy(addr, 7, 60, &val); + if (e->eee_enabled && (!!(val & (1 << 7)))) + e->eee_enabled = !!(val & (1 << 7)); + else + e->eee_enabled = 0; + + /* GPHY page to auto */ + write_phy(addr, 0xa42, 29, 0x0000); + + return 0; +} + +void rtl8380_rtl8218b_green_set(int mac, bool enable) +{ + u32 val; + + /* Set GPHY page to copper */ + write_phy(mac, 0xa42, 29, 0x0001); + + write_phy(mac, 0, 27, 0x8011); + read_phy(mac, 0, 28, &val); + if (enable) { + val |= 1 << 9; + write_phy(mac, 0, 27, 0x8011); + write_phy(mac, 0, 28, val); + } else { + val &= ~(1 << 9); + write_phy(mac, 0, 27, 0x8011); + write_phy(mac, 0, 28, val); + } + + /* GPHY page to auto */ + write_phy(mac, 0xa42, 29, 0x0000); +} + +int rtl8380_rtl8214fc_get_green(struct phy_device *phydev, struct ethtool_eee *e) +{ + u32 val; + int addr = phydev->mdio.addr; + + pr_debug("In %s %d\n", __func__, addr); + /* Set GPHY page to copper */ + write_phy(addr, 0xa42, 29, 0x0001); + + write_phy(addr, 0, 27, 0x8011); + read_phy(addr, 0, 28, &val); + if (e->eee_enabled && (!!(val & (1 << 9)))) + e->eee_enabled = !!(val & (1 << 9)); + else + e->eee_enabled = 0; + + /* GPHY page to auto */ + write_phy(addr, 0xa42, 29, 0x0000); + + return 0; +} + +static int rtl8380_rtl8214fc_set_eee(struct phy_device *phydev, + struct ethtool_eee *e) +{ + u32 pollMask; + int addr = phydev->mdio.addr; + + pr_debug("In %s port %d, enabled %d\n", __func__, addr, e->eee_enabled); + + if (rtl8380_rtl8214fc_media_is_fibre(addr)) { + netdev_err(phydev->attached_dev, "Port %d configured for FIBRE", addr); + return -ENOTSUPP; + } + + pollMask = sw_r32(RTL838X_SMI_POLL_CTRL); + sw_w32(0, RTL838X_SMI_POLL_CTRL); + rtl8380_rtl8218b_eee_set_u_boot(addr, (bool) e->eee_enabled); + sw_w32(pollMask, RTL838X_SMI_POLL_CTRL); + return 0; +} + +static int rtl8380_rtl8214fc_get_eee(struct phy_device *phydev, + struct ethtool_eee *e) +{ + int addr = phydev->mdio.addr; + + pr_debug("In %s port %d, enabled %d\n", __func__, addr, e->eee_enabled); + if (rtl8380_rtl8214fc_media_is_fibre(addr)) { + netdev_err(phydev->attached_dev, "Port %d configured for FIBRE", addr); + return -ENOTSUPP; + } + + return rtl8380_rtl8218b_get_eee_u_boot(phydev, e); +} + +static int rtl8380_rtl8218b_set_eee(struct phy_device *phydev, + struct ethtool_eee *e) +{ + u32 pollMask; + int addr = phydev->mdio.addr; + + pr_debug("In %s, port %d, enabled %d\n", __func__, addr, e->eee_enabled); + + pollMask = sw_r32(RTL838X_SMI_POLL_CTRL); + sw_w32(0, RTL838X_SMI_POLL_CTRL); + rtl8380_rtl8218b_eee_set_u_boot(addr, (bool) e->eee_enabled); + sw_w32(pollMask, RTL838X_SMI_POLL_CTRL); + + return 0; +} + +static int rtl8214c_match_phy_device(struct phy_device *phydev) +{ + return phydev->phy_id == PHY_ID_RTL8214C; +} + +static int rtl8380_configure_rtl8214c(struct phy_device *phydev) +{ + u32 phy_id, val; + int mac = phydev->mdio.addr; + + read_phy(mac, 0, 2, &val); + phy_id = val << 16; + read_phy(mac, 0, 3, &val); + phy_id |= val; + pr_debug("Phy on MAC %d: %x\n", mac, phy_id); + + phydev_info(phydev, "Detected external RTL8214C\n"); + + /* GPHY auto conf */ + write_phy(mac, 0xa42, 29, 0); + return 0; +} + +static int rtl8380_configure_rtl8214fc(struct phy_device *phydev) +{ + u32 phy_id, val, page = 0; + int i, l; + int mac = phydev->mdio.addr; + struct fw_header *h; + u32 *rtl8380_rtl8214fc_perchip; + u32 *rtl8380_rtl8214fc_perport; + + read_phy(mac, 0, 2, &val); + phy_id = val << 16; + read_phy(mac, 0, 3, &val); + phy_id |= val; + pr_debug("Phy on MAC %d: %x\n", mac, phy_id); + + /* Read internal PHY id */ + write_phy(mac, 0, 30, 0x0001); + write_phy(mac, 0, 31, 0x0a42); + write_phy(mac, 31, 27, 0x0002); + read_phy(mac, 31, 28, &val); + if (val != 0x6276) { + phydev_err(phydev, "Expected external RTL8214FC, found PHY-ID %x\n", val); + return -1; + } + phydev_info(phydev, "Detected external RTL8214FC\n"); + + h = rtl838x_request_fw(phydev, &rtl838x_8214fc_fw, FIRMWARE_838X_8214FC_1); + if (!h) + return -1; + + if (h->phy != 0x8214fc00) { + phydev_err(phydev, "Wrong firmware file: PHY mismatch.\n"); + return -1; + } + + rtl8380_rtl8214fc_perchip = (void *)h + sizeof(struct fw_header) + + h->parts[0].start; + + rtl8380_rtl8214fc_perport = (void *)h + sizeof(struct fw_header) + + h->parts[1].start; + + /* detect phy version */ + write_phy(mac, 0xfff, 27, 0x0004); + read_phy(mac, 0xfff, 28, &val); + + read_phy(mac, 0, 16, &val); + if (val & (1 << 11)) + rtl8380_rtl8214fc_on_off(mac, true); + else + rtl8380_phy_reset(mac); + + msleep(100); + write_phy(mac, 0, 30, 0x0001); + + i = 0; + while (rtl8380_rtl8214fc_perchip[i * 3] + && rtl8380_rtl8214fc_perchip[i * 3 + 1]) { + if (rtl8380_rtl8214fc_perchip[i * 3 + 1] == 0x1f) + page = rtl8380_rtl8214fc_perchip[i * 3 + 2]; + if (rtl8380_rtl8214fc_perchip[i * 3 + 1] == 0x13 && page == 0x260) { + read_phy(mac + rtl8380_rtl8214fc_perchip[i * 3], 0x260, 13, &val); + val = (val & 0x1f00) | (rtl8380_rtl8214fc_perchip[i * 3 + 2] + & 0xe0ff); + write_phy(mac + rtl8380_rtl8214fc_perchip[i * 3], + 0xfff, rtl8380_rtl8214fc_perchip[i * 3 + 1], val); + } else { + write_phy(mac + rtl8380_rtl8214fc_perchip[i * 3], + 0xfff, rtl8380_rtl8214fc_perchip[i * 3 + 1], + rtl8380_rtl8214fc_perchip[i * 3 + 2]); + } + i++; + } + + /* Force copper medium */ + for (i = 0; i < 4; i++) { + write_phy(mac + i, 0xfff, 0x1f, 0x0000); + write_phy(mac + i, 0xfff, 0x1e, 0x0001); + } + + /* Enable PHY */ + for (i = 0; i < 4; i++) { + write_phy(mac + i, 0xfff, 0x1f, 0x0000); + write_phy(mac + i, 0xfff, 0x00, 0x1140); + } + mdelay(100); + + /* Disable Autosensing */ + for (i = 0; i < 4; i++) { + for (l = 0; l < 100; l++) { + read_phy(mac + i, 0x0a42, 0x10, &val); + if ((val & 0x7) >= 3) + break; + } + if (l >= 100) { + phydev_err(phydev, "Could not disable autosensing\n"); + return -1; + } + } + + /* Request patch */ + for (i = 0; i < 4; i++) { + write_phy(mac + i, 0xfff, 0x1f, 0x0b82); + write_phy(mac + i, 0xfff, 0x10, 0x0010); + } + mdelay(300); + + /* Verify patch readiness */ + for (i = 0; i < 4; i++) { + for (l = 0; l < 100; l++) { + read_phy(mac + i, 0xb80, 0x10, &val); + if (val & 0x40) + break; + } + if (l >= 100) { + phydev_err(phydev, "Could not patch PHY\n"); + return -1; + } + } + + /* Use Broadcast ID method for patching */ + write_phy(mac, 0xfff, 0x1f, 0x0000); + write_phy(mac, 0xfff, 0x1d, 0x0008); + write_phy(mac, 0xfff, 0x1f, 0x0266); + write_phy(mac, 0xfff, 0x16, 0xff00 + mac); + write_phy(mac, 0xfff, 0x1f, 0x0000); + write_phy(mac, 0xfff, 0x1d, 0x0000); + mdelay(1); + + i = 0; + while (rtl8380_rtl8214fc_perport[i * 2]) { + write_phy(mac, 0xfff, rtl8380_rtl8214fc_perport[i * 2], + rtl8380_rtl8214fc_perport[i * 2 + 1]); + i++; + } + + /*Disable broadcast ID*/ + write_phy(mac, 0xfff, 0x1f, 0x0000); + write_phy(mac, 0xfff, 0x1d, 0x0008); + write_phy(mac, 0xfff, 0x1f, 0x0266); + write_phy(mac, 0xfff, 0x16, 0x00 + mac); + write_phy(mac, 0xfff, 0x1f, 0x0000); + write_phy(mac, 0xfff, 0x1d, 0x0000); + mdelay(1); + + /* Auto medium selection */ + for (i = 0; i < 4; i++) { + write_phy(mac + i, 0xfff, 0x1f, 0x0000); + write_phy(mac + i, 0xfff, 0x1e, 0x0000); + } + + return 0; +} + +static int rtl8214fc_match_phy_device(struct phy_device *phydev) +{ + int addr = phydev->mdio.addr; + + return phydev->phy_id == PHY_ID_RTL8214FC && addr >= 24; +} + +static int rtl8380_configure_serdes(struct phy_device *phydev) +{ + u32 v; + u32 sds_conf_value; + int i; + struct fw_header *h; + u32 *rtl8380_sds_take_reset; + u32 *rtl8380_sds_common; + u32 *rtl8380_sds01_qsgmii_6275b; + u32 *rtl8380_sds23_qsgmii_6275b; + u32 *rtl8380_sds4_fiber_6275b; + u32 *rtl8380_sds5_fiber_6275b; + u32 *rtl8380_sds_reset; + u32 *rtl8380_sds_release_reset; + + phydev_info(phydev, "Detected internal RTL8380 SERDES\n"); + + h = rtl838x_request_fw(phydev, &rtl838x_8218b_fw, FIRMWARE_838X_8380_1); + if (!h) + return -1; + + if (h->magic != 0x83808380) { + phydev_err(phydev, "Wrong firmware file: magic number mismatch.\n"); + return -1; + } + + rtl8380_sds_take_reset = (void *)h + sizeof(struct fw_header) + + h->parts[0].start; + + rtl8380_sds_common = (void *)h + sizeof(struct fw_header) + + h->parts[1].start; + + rtl8380_sds01_qsgmii_6275b = (void *)h + sizeof(struct fw_header) + + h->parts[2].start; + + rtl8380_sds23_qsgmii_6275b = (void *)h + sizeof(struct fw_header) + + h->parts[3].start; + + rtl8380_sds4_fiber_6275b = (void *)h + sizeof(struct fw_header) + + h->parts[4].start; + + rtl8380_sds5_fiber_6275b = (void *)h + sizeof(struct fw_header) + + h->parts[5].start; + + rtl8380_sds_reset = (void *)h + sizeof(struct fw_header) + + h->parts[6].start; + + rtl8380_sds_release_reset = (void *)h + sizeof(struct fw_header) + + h->parts[7].start; + + /* Back up serdes power off value */ + sds_conf_value = sw_r32(RTL838X_SDS_CFG_REG); + pr_info("SDS power down value: %x\n", sds_conf_value); + + /* take serdes into reset */ + i = 0; + while (rtl8380_sds_take_reset[2 * i]) { + sw_w32(rtl8380_sds_take_reset[2 * i + 1], rtl8380_sds_take_reset[2 * i]); + i++; + udelay(1000); + } + + /* apply common serdes patch */ + i = 0; + while (rtl8380_sds_common[2 * i]) { + sw_w32(rtl8380_sds_common[2 * i + 1], rtl8380_sds_common[2 * i]); + i++; + udelay(1000); + } + + /* internal R/W enable */ + sw_w32(3, RTL838X_INT_RW_CTRL); + + /* SerDes ports 4 and 5 are FIBRE ports */ + sw_w32_mask(0x7 | 0x38, 1 | (1 << 3), RTL838X_INT_MODE_CTRL); + + /* SerDes module settings, SerDes 0-3 are QSGMII */ + v = 0x6 << 25 | 0x6 << 20 | 0x6 << 15 | 0x6 << 10; + /* SerDes 4 and 5 are 1000BX FIBRE */ + v |= 0x4 << 5 | 0x4; + sw_w32(v, RTL838X_SDS_MODE_SEL); + + pr_info("PLL control register: %x\n", sw_r32(RTL838X_PLL_CML_CTRL)); + sw_w32_mask(0xfffffff0, 0xaaaaaaaf & 0xf, RTL838X_PLL_CML_CTRL); + i = 0; + while (rtl8380_sds01_qsgmii_6275b[2 * i]) { + sw_w32(rtl8380_sds01_qsgmii_6275b[2 * i + 1], + rtl8380_sds01_qsgmii_6275b[2 * i]); + i++; + } + + i = 0; + while (rtl8380_sds23_qsgmii_6275b[2 * i]) { + sw_w32(rtl8380_sds23_qsgmii_6275b[2 * i + 1], rtl8380_sds23_qsgmii_6275b[2 * i]); + i++; + } + + i = 0; + while (rtl8380_sds4_fiber_6275b[2 * i]) { + sw_w32(rtl8380_sds4_fiber_6275b[2 * i + 1], rtl8380_sds4_fiber_6275b[2 * i]); + i++; + } + + i = 0; + while (rtl8380_sds5_fiber_6275b[2 * i]) { + sw_w32(rtl8380_sds5_fiber_6275b[2 * i + 1], rtl8380_sds5_fiber_6275b[2 * i]); + i++; + } + + i = 0; + while (rtl8380_sds_reset[2 * i]) { + sw_w32(rtl8380_sds_reset[2 * i + 1], rtl8380_sds_reset[2 * i]); + i++; + } + + i = 0; + while (rtl8380_sds_release_reset[2 * i]) { + sw_w32(rtl8380_sds_release_reset[2 * i + 1], rtl8380_sds_release_reset[2 * i]); + i++; + } + + pr_info("SDS power down value now: %x\n", sw_r32(RTL838X_SDS_CFG_REG)); + sw_w32(sds_conf_value, RTL838X_SDS_CFG_REG); + + pr_info("Configuration of SERDES done\n"); + return 0; +} + +static int rtl8214fc_phy_probe(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + struct rtl838x_phy_priv *priv; + int addr = phydev->mdio.addr; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->name = "RTL8214FC"; + + /* All base addresses of the PHYs start at multiples of 8 */ + if (!(addr % 8)) { + /* Configuration must be done whil patching still possible */ + return rtl8380_configure_rtl8214fc(phydev); + } + return 0; +} + +static int rtl8214c_phy_probe(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + struct rtl838x_phy_priv *priv; + int addr = phydev->mdio.addr; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->name = "RTL8214C"; + + /* All base addresses of the PHYs start at multiples of 8 */ + if (!(addr % 8)) { + /* Configuration must be done whil patching still possible */ + return rtl8380_configure_rtl8214c(phydev); + } + return 0; +} + +static int rtl8218b_ext_phy_probe(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + struct rtl838x_phy_priv *priv; + int addr = phydev->mdio.addr; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->name = "RTL8218B (external)"; + + /* All base addresses of the PHYs start at multiples of 8 */ + if (!(addr % 8)) { + /* Configuration must be done while patching still possible */ + return rtl8380_configure_ext_rtl8218b(phydev); + } + return 0; +} + +static int rtl8218b_int_phy_probe(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + struct rtl838x_phy_priv *priv; + int addr = phydev->mdio.addr; + + if (soc_info.family != RTL8380_FAMILY_ID) + return -ENODEV; + if (addr >= 24) + return -ENODEV; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->name = "RTL8218B (internal)"; + + /* All base addresses of the PHYs start at multiples of 8 */ + if (!(addr % 8)) { + /* Configuration must be done while patching still possible */ + return rtl8380_configure_int_rtl8218b(phydev); + } + return 0; +} + +static int rtl838x_serdes_probe(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + struct rtl838x_phy_priv *priv; + int addr = phydev->mdio.addr; + + if (soc_info.family != RTL8380_FAMILY_ID) + return -ENODEV; + if (addr < 24) + return -ENODEV; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->name = "RTL8380 Serdes"; + + /* On the RTL8380M, PHYs 24-27 connect to the internal SerDes */ + if (soc_info.id == 0x8380) { + if (addr == 24) + return rtl8380_configure_serdes(phydev); + return 0; + } + return -ENODEV; +} + +static int rtl8390_serdes_probe(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + struct rtl838x_phy_priv *priv; + int addr = phydev->mdio.addr; + + if (soc_info.family != RTL8390_FAMILY_ID) + return -ENODEV; + + if (addr < 24) + return -ENODEV; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->name = "RTL8390 Serdes"; + return rtl8390_configure_generic(phydev); +} + +static struct phy_driver rtl838x_phy_driver[] = { + { + PHY_ID_MATCH_MODEL(PHY_ID_RTL8214FC), + .name = "REATLTEK RTL8214C", + .features = PHY_GBIT_FEATURES, + .match_phy_device = rtl8214c_match_phy_device, + .probe = rtl8214c_phy_probe, + .suspend = genphy_suspend, + .resume = genphy_resume, + .set_loopback = genphy_loopback, + }, + { + PHY_ID_MATCH_MODEL(PHY_ID_RTL8214FC), + .name = "REATLTEK RTL8214FC", + .features = PHY_GBIT_FIBRE_FEATURES, + .match_phy_device = rtl8214fc_match_phy_device, + .probe = rtl8214fc_phy_probe, + .suspend = genphy_suspend, + .resume = genphy_resume, + .set_loopback = genphy_loopback, + .read_mmd = rtl8380_rtl8218b_read_mmd, + .write_mmd = rtl8380_rtl8218b_write_mmd, + .set_port = rtl8380_rtl8214fc_set_port, + .get_port = rtl8380_rtl8214fc_get_port, + .set_eee = rtl8380_rtl8214fc_set_eee, + .get_eee = rtl8380_rtl8214fc_get_eee, + }, + { + PHY_ID_MATCH_MODEL(PHY_ID_RTL8218B_E), + .name = "REATLTEK RTL8218B (external)", + .features = PHY_GBIT_FEATURES, + .match_phy_device = rtl8218b_ext_match_phy_device, + .probe = rtl8218b_ext_phy_probe, + .suspend = genphy_suspend, + .resume = genphy_resume, + .set_loopback = genphy_loopback, + .read_mmd = rtl8380_rtl8218b_read_mmd, + .write_mmd = rtl8380_rtl8218b_write_mmd, + .set_eee = rtl8380_rtl8218b_set_eee, + .get_eee = rtl8380_rtl8218b_get_eee_u_boot, + }, + { + PHY_ID_MATCH_MODEL(PHY_ID_RTL8218B_I), + .name = "REATLTEK RTL8218B (internal)", + .features = PHY_GBIT_FEATURES, + .probe = rtl8218b_int_phy_probe, + .suspend = genphy_suspend, + .resume = genphy_resume, + .set_loopback = genphy_loopback, + .read_mmd = rtl8380_rtl8218b_read_mmd, + .write_mmd = rtl8380_rtl8218b_write_mmd, + .set_eee = rtl8380_rtl8218b_set_eee, + .get_eee = rtl8380_rtl8218b_get_eee_u_boot, + }, + { + PHY_ID_MATCH_MODEL(PHY_ID_RTL8218B_I), + .name = "REATLTEK RTL8380 SERDES", + .features = PHY_GBIT_FIBRE_FEATURES, + .probe = rtl838x_serdes_probe, + .suspend = genphy_suspend, + .resume = genphy_resume, + .set_loopback = genphy_loopback, + .read_mmd = rtl8380_rtl8218b_read_mmd, + .write_mmd = rtl8380_rtl8218b_write_mmd, + }, + { + PHY_ID_MATCH_MODEL(PHY_ID_RTL8390_GENERIC), + .name = "REATLTEK RTL8390 Generic", + .features = PHY_GBIT_FIBRE_FEATURES, + .probe = rtl8390_serdes_probe, + .suspend = genphy_suspend, + .resume = genphy_resume, + .set_loopback = genphy_loopback, + } +}; + +module_phy_driver(rtl838x_phy_driver); + +static struct mdio_device_id __maybe_unused rtl838x_tbl[] = { + { PHY_ID_MATCH_MODEL(PHY_ID_RTL8214FC) }, + { } +}; + +MODULE_DEVICE_TABLE(mdio, rtl838x_tbl); + +MODULE_AUTHOR("B. Koblitz"); +MODULE_DESCRIPTION("RTL838x PHY driver"); +MODULE_LICENSE("GPL"); diff --git a/target/linux/rtl838x/files-5.4/drivers/net/dsa/rtl838x_sw.c b/target/linux/rtl838x/files-5.4/drivers/net/dsa/rtl838x_sw.c new file mode 100644 index 0000000000..7f6c2d6a29 --- /dev/null +++ b/target/linux/rtl838x/files-5.4/drivers/net/dsa/rtl838x_sw.c @@ -0,0 +1,2150 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "rtl838x.h" + +#define RTL8380_VERSION_A 'A' +#define RTL8390_VERSION_A 'A' +#define RTL8380_VERSION_B 'B' + +DEFINE_MUTEX(smi_lock); + +#define MIB_DESC(_size, _offset, _name) {.size = _size, .offset = _offset, .name = _name} +struct rtl838x_mib_desc { + unsigned int size; + unsigned int offset; + const char *name; +}; + +inline void rtl838x_mask_port_reg(u64 clear, u64 set, int reg) +{ + sw_w32_mask((u32)clear, (u32)set, reg); +} + +inline void rtl839x_mask_port_reg(u64 clear, u64 set, int reg) +{ + sw_w32_mask((u32) (clear >> 32), (u32) (set >> 32), reg); + sw_w32_mask((u32) (clear & 0xffffffff), (u32) (set & 0xffffffff), reg + 4); +} + +inline void rtl838x_set_port_reg(u64 set, int reg) +{ + sw_w32(set, reg); +} + +inline void rtl839x_set_port_reg(u64 set, int reg) +{ + sw_w32(set >> 32, reg); + sw_w32(set & 0xffffffff, reg + 4); +} + +inline u64 rtl838x_get_port_reg(int reg) +{ + return ((u64) sw_r32(reg)); +} + +inline u64 rtl839x_get_port_reg(int reg) +{ + u64 v = sw_r32(reg); + + v <<= 32; + v |= sw_r32(reg + 4); + return v; +} + +inline int rtl838x_stat_port_std_mib(int p) +{ + return RTL838X_STAT_PORT_STD_MIB + (p << 8); +} + +inline int rtl839x_stat_port_std_mib(int p) +{ + return RTL839X_STAT_PORT_STD_MIB + (p << 8); +} + +inline void rtl838x_mask_port_iso_ctrl(u64 clear, u64 set, int port) +{ + sw_w32_mask(clear, set, RTL838X_PORT_ISO_CTRL(port)); +} + +inline void rtl839x_mask_port_iso_ctrl(u64 clear, u64 set, int port) +{ + sw_w32_mask(clear >> 32, set >> 32, RTL839X_PORT_ISO_CTRL(port)); + sw_w32_mask(clear & 0xffffffff, set & 0xffffffff, + RTL839X_PORT_ISO_CTRL(port) + 4); +} + +inline void rtl838x_set_port_iso_ctrl(u64 set, int port) +{ + sw_w32(set, RTL838X_PORT_ISO_CTRL(port)); +} + +inline void rtl839x_set_port_iso_ctrl(u64 set, int port) +{ + sw_w32(set >> 32, RTL839X_PORT_ISO_CTRL(port)); + sw_w32(set & 0xffffffff, RTL839X_PORT_ISO_CTRL(port) + 4); +} + +inline void rtl838x_exec_tbl0_cmd(u32 cmd) +{ + sw_w32(cmd, RTL838X_TBL_ACCESS_CTRL_0); + do { } while (sw_r32(RTL838X_TBL_ACCESS_CTRL_0) & (1 << 15)); +} + +inline void rtl839x_exec_tbl0_cmd(u32 cmd) +{ + sw_w32(cmd, RTL839X_TBL_ACCESS_CTRL_0); + do { } while (sw_r32(RTL839X_TBL_ACCESS_CTRL_0) & (1 << 16)); +} + +inline void rtl838x_exec_tbl1_cmd(u32 cmd) +{ + sw_w32(cmd, RTL838X_TBL_ACCESS_CTRL_1); + do { } while (sw_r32(RTL838X_TBL_ACCESS_CTRL_1) & (1 << 15)); +} + +inline void rtl839x_exec_tbl1_cmd(u32 cmd) +{ + sw_w32(cmd, RTL839X_TBL_ACCESS_CTRL_1); + do { } while (sw_r32(RTL839X_TBL_ACCESS_CTRL_1) & (1 << 16)); +} + +inline int rtl838x_tbl_access_data_0(int i) +{ + return RTL838X_TBL_ACCESS_DATA_0(i); +} + +inline int rtl839x_tbl_access_data_0(int i) +{ + return RTL839X_TBL_ACCESS_DATA_0(i); +} + +static void rtl839x_vlan_tables_read(u32 vlan, struct rtl838x_vlan_info *info) +{ + u32 cmd; + u64 v; + + cmd = 1 << 16 /* Execute cmd */ + | 0 << 15 /* Read */ + | 0 << 12 /* Table type 0b000 */ + | (vlan & 0xfff); + rtl839x_exec_tbl0_cmd(cmd); + + v = sw_r32(RTL838X_TBL_ACCESS_DATA_0(0)); + v <<= 32; + v |= sw_r32(RTL838X_TBL_ACCESS_DATA_0(1)); + info->tagged_ports = v >> 11; + info->vlan_conf = (v & 0x7ff) << 2; + info->vlan_conf |= sw_r32(RTL838X_TBL_ACCESS_DATA_0(1)) >> 30; + + cmd = 1 << 16 /* Execute cmd */ + | 0 << 15 /* Read */ + | 0 << 12 /* Table type 0b000 */ + | (vlan & 0xfff); + rtl839x_exec_tbl1_cmd(cmd); + v = sw_r32(RTL838X_TBL_ACCESS_DATA_1(0)); + v <<= 32; + v |= sw_r32(RTL838X_TBL_ACCESS_DATA_1(1)); + info->untagged_ports = v >> 11; +} + +static void rtl838x_vlan_tables_read(u32 vlan, struct rtl838x_vlan_info *info) +{ + u32 cmd; + + cmd = 1 << 15 /* Execute cmd */ + | 1 << 14 /* Read */ + | 0 << 12 /* Table type 0b00 */ + | (vlan & 0xfff); + rtl838x_exec_tbl0_cmd(cmd); + info->tagged_ports = sw_r32(RTL838X_TBL_ACCESS_DATA_0(0)); + info->vlan_conf = sw_r32(RTL838X_TBL_ACCESS_DATA_0(1)); + + cmd = 1 << 15 /* Execute cmd */ + | 1 << 14 /* Read */ + | 0 << 12 /* Table type 0b00 */ + | (vlan & 0xfff); + rtl838x_exec_tbl1_cmd(cmd); + info->untagged_ports = sw_r32(RTL838X_TBL_ACCESS_DATA_1(0)); +} + +static void rtl839x_vlan_set_tagged(u32 vlan, u64 portmask, u32 conf) +{ + u32 cmd = 1 << 16 /* Execute cmd */ + | 1 << 15 /* Write */ + | 0 << 12 /* Table type 0b00 */ + | (vlan & 0xfff); + u64 v = portmask << 11; + + v |= (conf >> 2) & 0x7ff; + sw_w64(v, RTL838X_TBL_ACCESS_DATA_0(0)); + sw_w32(conf << 30, RTL838X_TBL_ACCESS_DATA_0(2)); + rtl839x_exec_tbl0_cmd(cmd); +} + +static void rtl838x_vlan_set_tagged(u32 vlan, u64 portmask, u32 conf) +{ + u32 cmd = 1 << 15 /* Execute cmd */ + | 0 << 14 /* Write */ + | 0 << 12 /* Table type 0b00 */ + | (vlan & 0xfff); + + sw_w32(portmask, RTL838X_TBL_ACCESS_DATA_0(0)); + sw_w32(conf, RTL838X_TBL_ACCESS_DATA_0(1)); + rtl838x_exec_tbl0_cmd(cmd); +} + +static void rtl839x_vlan_set_untagged(u32 vlan, u64 portmask) +{ + u32 cmd = 1 << 16 /* Execute cmd */ + | 1 << 15 /* Write */ + | 0 << 12 /* Table type 0b00 */ + | (vlan & 0xfff); + sw_w64(portmask << 11, RTL838X_TBL_ACCESS_DATA_1(0)); + rtl839x_exec_tbl1_cmd(cmd); +} + +static void rtl838x_vlan_set_untagged(u32 vlan, u64 portmask) +{ + u32 cmd = 1 << 15 /* Execute cmd */ + | 0 << 14 /* Write */ + | 0 << 12 /* Table type 0b00 */ + | (vlan & 0xfff); + sw_w32(portmask & 0x1fffffff, RTL838X_TBL_ACCESS_DATA_1(0)); + rtl838x_exec_tbl1_cmd(cmd); +} + +static inline int rtl838x_mac_force_mode_ctrl(int p) +{ + return RTL838X_MAC_FORCE_MODE_CTRL + (p << 2); +} + +static inline int rtl839x_mac_force_mode_ctrl(int p) +{ + return RTL839X_MAC_FORCE_MODE_CTRL + (p << 2); +} + +static const struct rtl838x_reg rtl838x_reg = { + .mask_port_reg = rtl838x_mask_port_reg, + .set_port_reg = rtl838x_set_port_reg, + .get_port_reg = rtl838x_get_port_reg, + .stat_port_rst = RTL838X_STAT_PORT_RST, + .stat_rst = RTL838X_STAT_RST, + .stat_port_std_mib = rtl838x_stat_port_std_mib, + .mask_port_iso_ctrl = rtl838x_mask_port_iso_ctrl, + .set_port_iso_ctrl = rtl838x_set_port_iso_ctrl, + .l2_ctrl_0 = RTL838X_L2_CTRL_0, + .l2_ctrl_1 = RTL838X_L2_CTRL_1, + .l2_port_aging_out = RTL838X_L2_PORT_AGING_OUT, + .smi_poll_ctrl = RTL838X_SMI_POLL_CTRL, + .l2_tbl_flush_ctrl = RTL838X_L2_TBL_FLUSH_CTRL, + .exec_tbl0_cmd = rtl838x_exec_tbl0_cmd, + .exec_tbl1_cmd = rtl838x_exec_tbl1_cmd, + .tbl_access_data_0 = rtl838x_tbl_access_data_0, + .isr_glb_src = RTL838X_ISR_GLB_SRC, + .isr_port_link_sts_chg = RTL838X_ISR_PORT_LINK_STS_CHG, + .imr_port_link_sts_chg = RTL838X_IMR_PORT_LINK_STS_CHG, + .imr_glb = RTL838X_IMR_GLB, + .vlan_tables_read = rtl838x_vlan_tables_read, + .vlan_set_tagged = rtl838x_vlan_set_tagged, + .vlan_set_untagged = rtl838x_vlan_set_untagged, + .mac_force_mode_ctrl = rtl838x_mac_force_mode_ctrl, + .rst_glb_ctrl = RTL838X_RST_GLB_CTRL_0, +}; + +static const struct rtl838x_reg rtl839x_reg = { + .mask_port_reg = rtl839x_mask_port_reg, + .set_port_reg = rtl839x_set_port_reg, + .get_port_reg = rtl839x_get_port_reg, + .stat_port_rst = RTL839X_STAT_PORT_RST, + .stat_rst = RTL839X_STAT_RST, + .stat_port_std_mib = rtl839x_stat_port_std_mib, + .mask_port_iso_ctrl = rtl839x_mask_port_iso_ctrl, + .set_port_iso_ctrl = rtl839x_set_port_iso_ctrl, + .l2_ctrl_0 = RTL839X_L2_CTRL_0, + .l2_ctrl_1 = RTL839X_L2_CTRL_1, + .l2_port_aging_out = RTL839X_L2_PORT_AGING_OUT, + .smi_poll_ctrl = RTL839X_SMI_PORT_POLLING_CTRL, + .l2_tbl_flush_ctrl = RTL839X_L2_TBL_FLUSH_CTRL, + .exec_tbl0_cmd = rtl839x_exec_tbl0_cmd, + .exec_tbl1_cmd = rtl839x_exec_tbl1_cmd, + .tbl_access_data_0 = rtl839x_tbl_access_data_0, + .isr_glb_src = RTL839X_ISR_GLB_SRC, + .isr_port_link_sts_chg = RTL839X_ISR_PORT_LINK_STS_CHG, + .imr_port_link_sts_chg = RTL839X_IMR_PORT_LINK_STS_CHG, + .imr_glb = RTL839X_IMR_GLB, + .vlan_tables_read = rtl839x_vlan_tables_read, + .vlan_set_tagged = rtl839x_vlan_set_tagged, + .vlan_set_untagged = rtl839x_vlan_set_untagged, + .mac_force_mode_ctrl = rtl839x_mac_force_mode_ctrl, + .rst_glb_ctrl = RTL839X_RST_GLB_CTRL, +}; + +static const struct rtl838x_mib_desc rtl838x_mib[] = { + MIB_DESC(2, 0xf8, "ifInOctets"), + MIB_DESC(2, 0xf0, "ifOutOctets"), + MIB_DESC(1, 0xec, "dot1dTpPortInDiscards"), + MIB_DESC(1, 0xe8, "ifInUcastPkts"), + MIB_DESC(1, 0xe4, "ifInMulticastPkts"), + MIB_DESC(1, 0xe0, "ifInBroadcastPkts"), + MIB_DESC(1, 0xdc, "ifOutUcastPkts"), + MIB_DESC(1, 0xd8, "ifOutMulticastPkts"), + MIB_DESC(1, 0xd4, "ifOutBroadcastPkts"), + MIB_DESC(1, 0xd0, "ifOutDiscards"), + MIB_DESC(1, 0xcc, ".3SingleCollisionFrames"), + MIB_DESC(1, 0xc8, ".3MultipleCollisionFrames"), + MIB_DESC(1, 0xc4, ".3DeferredTransmissions"), + MIB_DESC(1, 0xc0, ".3LateCollisions"), + MIB_DESC(1, 0xbc, ".3ExcessiveCollisions"), + MIB_DESC(1, 0xb8, ".3SymbolErrors"), + MIB_DESC(1, 0xb4, ".3ControlInUnknownOpcodes"), + MIB_DESC(1, 0xb0, ".3InPauseFrames"), + MIB_DESC(1, 0xac, ".3OutPauseFrames"), + MIB_DESC(1, 0xa8, "DropEvents"), + MIB_DESC(1, 0xa4, "tx_BroadcastPkts"), + MIB_DESC(1, 0xa0, "tx_MulticastPkts"), + MIB_DESC(1, 0x9c, "CRCAlignErrors"), + MIB_DESC(1, 0x98, "tx_UndersizePkts"), + MIB_DESC(1, 0x94, "rx_UndersizePkts"), + MIB_DESC(1, 0x90, "rx_UndersizedropPkts"), + MIB_DESC(1, 0x8c, "tx_OversizePkts"), + MIB_DESC(1, 0x88, "rx_OversizePkts"), + MIB_DESC(1, 0x84, "Fragments"), + MIB_DESC(1, 0x80, "Jabbers"), + MIB_DESC(1, 0x7c, "Collisions"), + MIB_DESC(1, 0x78, "tx_Pkts64Octets"), + MIB_DESC(1, 0x74, "rx_Pkts64Octets"), + MIB_DESC(1, 0x70, "tx_Pkts65to127Octets"), + MIB_DESC(1, 0x6c, "rx_Pkts65to127Octets"), + MIB_DESC(1, 0x68, "tx_Pkts128to255Octets"), + MIB_DESC(1, 0x64, "rx_Pkts128to255Octets"), + MIB_DESC(1, 0x60, "tx_Pkts256to511Octets"), + MIB_DESC(1, 0x5c, "rx_Pkts256to511Octets"), + MIB_DESC(1, 0x58, "tx_Pkts512to1023Octets"), + MIB_DESC(1, 0x54, "rx_Pkts512to1023Octets"), + MIB_DESC(1, 0x50, "tx_Pkts1024to1518Octets"), + MIB_DESC(1, 0x4c, "rx_StatsPkts1024to1518Octets"), + MIB_DESC(1, 0x48, "tx_Pkts1519toMaxOctets"), + MIB_DESC(1, 0x44, "rx_Pkts1519toMaxOctets"), + MIB_DESC(1, 0x40, "rxMacDiscards") +}; + +static irqreturn_t rtl838x_switch_irq(int irq, void *dev_id) +{ + struct dsa_switch *ds = dev_id; + u32 status = sw_r32(RTL838X_ISR_GLB_SRC); + u32 ports = sw_r32(RTL838X_ISR_PORT_LINK_STS_CHG); + u32 link; + int i; + + /* Clear status */ + sw_w32(ports, RTL838X_ISR_PORT_LINK_STS_CHG); + pr_info("Link change: status: %x, ports %x\n", status, ports); + + for (i = 0; i < 28; i++) { + if (ports & (1 << i)) { + link = sw_r32(RTL838X_MAC_LINK_STS); + if (link & (1 << i)) + dsa_port_phylink_mac_change(ds, i, true); + else + dsa_port_phylink_mac_change(ds, i, false); + } + } + return IRQ_HANDLED; +} + +static irqreturn_t rtl839x_switch_irq(int irq, void *dev_id) +{ + struct dsa_switch *ds = dev_id; + u32 status = sw_r32(RTL839X_ISR_GLB_SRC); + u64 ports = sw_r64(RTL839X_ISR_PORT_LINK_STS_CHG); + u64 link; + int i; + + /* Clear status */ + sw_w64(ports, RTL839X_ISR_PORT_LINK_STS_CHG); + pr_info("Link change: status: %x, ports %llx\n", status, ports); + + for (i = 0; i < 52; i++) { + if (ports & (1 << i)) { + link = sw_r64(RTL839X_MAC_LINK_STS); + if (link & (1 << i)) + dsa_port_phylink_mac_change(ds, i, true); + else + dsa_port_phylink_mac_change(ds, i, false); + } + } + return IRQ_HANDLED; +} + +int rtl8380_sds_power(int mac, int val) +{ + u32 mode = (val == 1) ? 0x4 : 0x9; + u32 offset = (mac == 24) ? 5 : 0; + + if ((mac != 24) && (mac != 26)) { + pr_err("%s: not a fibre port: %d\n", __func__, mac); + return -1; + } + + sw_w32_mask(0x1f << offset, mode << offset, RTL838X_SDS_MODE_SEL); + + rtl8380_sds_rst(mac); + + return 0; +} + +static int rtl838x_smi_wait_op(int timeout) +{ + do { + timeout--; + udelay(10); + } while ((sw_r32(RTL838X_SMI_ACCESS_PHY_CTRL_1) & 0x1) && (timeout >= 0)); + if (timeout <= 0) + return -1; + return 0; +} + +/* + * Write to a register in a page of the PHY + */ +int rtl838x_write_phy(u32 port, u32 page, u32 reg, u32 val) +{ + u32 v; + u32 park_page; + + val &= 0xffff; + if (port > 31 || page > 4095 || reg > 31) + return -ENOTSUPP; + + mutex_lock(&smi_lock); + if (rtl838x_smi_wait_op(10000)) + goto timeout; + + sw_w32(1 << port, RTL838X_SMI_ACCESS_PHY_CTRL_0); + mdelay(10); + + sw_w32_mask(0xffff0000, val << 16, RTL838X_SMI_ACCESS_PHY_CTRL_2); + + park_page = sw_r32(RTL838X_SMI_ACCESS_PHY_CTRL_1) & ((0x1f << 15) | 0x2); + v = reg << 20 | page << 3 | 0x4; + sw_w32(v | park_page, RTL838X_SMI_ACCESS_PHY_CTRL_1); + sw_w32_mask(0, 1, RTL838X_SMI_ACCESS_PHY_CTRL_1); + + if (rtl838x_smi_wait_op(10000)) + goto timeout; + + mutex_unlock(&smi_lock); + return 0; + +timeout: + mutex_unlock(&smi_lock); + return -ETIMEDOUT; +} + +int rtl839x_write_phy(u32 port, u32 page, u32 reg, u32 val) +{ + u32 v; + int err = 0; + + val &= 0xffff; + if (port > 63 || page > 4095 || reg > 31) + return -ENOTSUPP; + + mutex_lock(&smi_lock); + /* Clear both port registers */ + sw_w32(0, RTL839X_PHYREG_PORT_CTRL(0)); + sw_w32(0, RTL839X_PHYREG_PORT_CTRL(0) + 4); + sw_w32_mask(0, 1 << port, RTL839X_PHYREG_PORT_CTRL(port)); + + sw_w32_mask(0xffff0000, val << 16, RTL839X_PHYREG_DATA_CTRL); + + v = reg << 5 | page << 10 | ((page == 0x1fff) ? 0x1f : 0) << 23; + sw_w32(v, RTL839X_PHYREG_ACCESS_CTRL); + + sw_w32(0x1ff, RTL839X_PHYREG_CTRL); + + v |= 1 << 3 | 1; /* Write operation and execute */ + sw_w32(v, RTL839X_PHYREG_ACCESS_CTRL); + + do { + } while (sw_r32(RTL839X_PHYREG_ACCESS_CTRL) & 0x1); + + if (sw_r32(RTL839X_PHYREG_ACCESS_CTRL) & 0x2) + err = -EIO; + + mutex_unlock(&smi_lock); + return err; +} + +/* + * Reads a register in a page from the PHY + */ +int rtl838x_read_phy(u32 port, u32 page, u32 reg, u32 *val) +{ + u32 v; + u32 park_page; + + if (port > 31 || page > 4095 || reg > 31) + return -ENOTSUPP; + + mutex_lock(&smi_lock); + + if (rtl838x_smi_wait_op(10000)) + goto timeout; + + sw_w32_mask(0xffff0000, port << 16, RTL838X_SMI_ACCESS_PHY_CTRL_2); + + park_page = sw_r32(RTL838X_SMI_ACCESS_PHY_CTRL_1) & ((0x1f << 15) | 0x2); + v = reg << 20 | page << 3; + sw_w32(v | park_page, RTL838X_SMI_ACCESS_PHY_CTRL_1); + sw_w32_mask(0, 1, RTL838X_SMI_ACCESS_PHY_CTRL_1); + + if (rtl838x_smi_wait_op(10000)) + goto timeout; + + *val = sw_r32(RTL838X_SMI_ACCESS_PHY_CTRL_2) & 0xffff; + + mutex_unlock(&smi_lock); + return 0; + +timeout: + mutex_unlock(&smi_lock); + return -ETIMEDOUT; +} + +int rtl839x_read_phy(u32 port, u32 page, u32 reg, u32 *val) +{ + u32 v; + + if (port > 63 || page > 4095 || reg > 31) + return -ENOTSUPP; + + mutex_lock(&smi_lock); + + sw_w32_mask(0xffff0000, port << 16, RTL839X_PHYREG_DATA_CTRL); + v = reg << 5 | page << 10 | ((page == 0x1fff) ? 0x1f : 0) << 23; + sw_w32(v, RTL839X_PHYREG_ACCESS_CTRL); + + sw_w32(0x1ff, RTL839X_PHYREG_CTRL); + + v |= 1; + sw_w32(v, RTL839X_PHYREG_ACCESS_CTRL); + + do { + } while (sw_r32(RTL839X_PHYREG_ACCESS_CTRL) & 0x1); + + *val = sw_r32(RTL839X_PHYREG_DATA_CTRL) & 0xffff; + + mutex_unlock(&smi_lock); + return 0; +} + +static int read_phy(u32 port, u32 page, u32 reg, u32 *val) +{ + if (soc_info.family == RTL8390_FAMILY_ID) + return rtl839x_read_phy(port, page, reg, val); + else + return rtl838x_read_phy(port, page, reg, val); +} + +static int write_phy(u32 port, u32 page, u32 reg, u32 val) +{ + if (soc_info.family == RTL8390_FAMILY_ID) + return rtl839x_write_phy(port, page, reg, val); + else + return rtl838x_write_phy(port, page, reg, val); +} + +/* + * Write to an mmd register of the PHY + */ +int rtl838x_write_mmd_phy(u32 port, u32 addr, u32 reg, u32 val) +{ + u32 v; + + pr_debug("MMD write: port %d, dev %d, reg %d, val %x\n", port, addr, reg, val); + val &= 0xffff; + mutex_lock(&smi_lock); + + if (rtl838x_smi_wait_op(10000)) + goto timeout; + + sw_w32(1 << port, RTL838X_SMI_ACCESS_PHY_CTRL_0); + mdelay(10); + + sw_w32_mask(0xffff0000, val << 16, RTL838X_SMI_ACCESS_PHY_CTRL_2); + + sw_w32_mask(0x1f << 16, addr << 16, RTL838X_SMI_ACCESS_PHY_CTRL_3); + sw_w32_mask(0xffff, reg, RTL838X_SMI_ACCESS_PHY_CTRL_3); + /* mmd-access | write | cmd-start */ + v = 1 << 1 | 1 << 2 | 1; + sw_w32(v, RTL838X_SMI_ACCESS_PHY_CTRL_1); + + if (rtl838x_smi_wait_op(10000)) + goto timeout; + + mutex_unlock(&smi_lock); + return 0; + +timeout: + mutex_unlock(&smi_lock); + return -ETIMEDOUT; +} + +/* + * Read to an mmd register of the PHY + */ +int rtl838x_read_mmd_phy(u32 port, u32 addr, u32 reg, u32 *val) +{ + u32 v; + + mutex_lock(&smi_lock); + + if (rtl838x_smi_wait_op(10000)) + goto timeout; + + sw_w32(1 << port, RTL838X_SMI_ACCESS_PHY_CTRL_0); + mdelay(10); + + sw_w32_mask(0xffff0000, port << 16, RTL838X_SMI_ACCESS_PHY_CTRL_2); + + v = addr << 16 | reg; + sw_w32(v, RTL838X_SMI_ACCESS_PHY_CTRL_3); + + /* mmd-access | read | cmd-start */ + v = 1 << 1 | 0 << 2 | 1; + sw_w32(v, RTL838X_SMI_ACCESS_PHY_CTRL_1); + + if (rtl838x_smi_wait_op(10000)) + goto timeout; + + *val = sw_r32(RTL838X_SMI_ACCESS_PHY_CTRL_2) & 0xffff; + + mutex_unlock(&smi_lock); + return 0; + +timeout: + mutex_unlock(&smi_lock); + return -ETIMEDOUT; +} + +static void rtl8380_get_version(struct rtl838x_switch_priv *priv) +{ + u32 rw_save, info_save; + u32 info; + + if (priv->id) + pr_debug("SoC ID: %4x: %s\n", priv->id, soc_info.name); + else + pr_err("Unknown chip id (%04x)\n", priv->id); + + rw_save = sw_r32(RTL838X_INT_RW_CTRL); + sw_w32(rw_save | 0x3, RTL838X_INT_RW_CTRL); + + info_save = sw_r32(RTL838X_CHIP_INFO); + sw_w32(info_save | 0xA0000000, RTL838X_CHIP_INFO); + + info = sw_r32(RTL838X_CHIP_INFO); + sw_w32(info_save, RTL838X_CHIP_INFO); + sw_w32(rw_save, RTL838X_INT_RW_CTRL); + + if ((info & 0xFFFF) == 0x6275) { + if (((info >> 16) & 0x1F) == 0x1) + priv->version = RTL8380_VERSION_A; + else if (((info >> 16) & 0x1F) == 0x2) + priv->version = RTL8380_VERSION_B; + else + priv->version = RTL8380_VERSION_B; + } else { + priv->version = '-'; + } +} + +static void rtl8390_get_version(struct rtl838x_switch_priv *priv) +{ + u32 info; + + sw_w32_mask(0xf << 28, 0xa << 28, RTL839X_CHIP_INFO); + info = sw_r32(RTL839X_CHIP_INFO); + pr_info("Chip-Info: %x\n", info); + priv->version = RTL8390_VERSION_A; +} + +int dsa_phy_read(struct dsa_switch *ds, int phy_addr, int phy_reg) +{ + u32 val; + u32 offset = 0; + struct rtl838x_switch_priv *priv = ds->priv; + + if (phy_addr >= 24 && phy_addr <= 27 + && priv->ports[24].phy == PHY_RTL838X_SDS) { + if (phy_addr == 26) + offset = 0x100; + val = sw_r32(MAPLE_SDS4_FIB_REG0r + offset + (phy_reg << 2)) & 0xffff; + return val; + } + + read_phy(phy_addr, 0, phy_reg, &val); + return val; +} + +int dsa_phy_write(struct dsa_switch *ds, int phy_addr, int phy_reg, u16 val) +{ + u32 offset = 0; + struct rtl838x_switch_priv *priv = ds->priv; + + if (phy_addr >= 24 && phy_addr <= 27 + && priv->ports[24].phy == PHY_RTL838X_SDS) { + if (phy_addr == 26) + offset = 0x100; + sw_w32(val, MAPLE_SDS4_FIB_REG0r + offset + (phy_reg << 2)); + return 0; + } + return write_phy(phy_addr, 0, phy_reg, val); +} + +static int rtl838x_mdio_read(struct mii_bus *bus, int addr, int regnum) +{ + int ret; + struct rtl838x_switch_priv *priv = bus->priv; + + ret = dsa_phy_read(priv->ds, addr, regnum); + return ret; +} + +static int rtl838x_mdio_write(struct mii_bus *bus, int addr, int regnum, + u16 val) +{ + struct rtl838x_switch_priv *priv = bus->priv; + + return dsa_phy_write(priv->ds, addr, regnum, val); +} + +static void rtl838x_enable_phy_polling(struct rtl838x_switch_priv *priv) +{ + int i; + u64 v = 0; + + msleep(1000); + /* Enable all ports with a PHY, including the SFP-ports */ + for (i = 0; i < priv->cpu_port; i++) { + if (priv->ports[i].phy) + v |= 1 << i; + } + + pr_info("%s: %16llx\n", __func__, v); + priv->r->set_port_reg(v, priv->r->smi_poll_ctrl); + + /* PHY update complete */ + if (priv->family_id == RTL8390_FAMILY_ID) + sw_w32_mask(1 << 7, 0, RTL839X_SMI_GLB_CTRL); + else + sw_w32_mask(0, 0x8000, RTL838X_SMI_GLB_CTRL); +} + +void rtl839x_print_matrix(void) +{ + volatile u64 *ptr = RTL838X_SW_BASE + RTL839X_PORT_ISO_CTRL(0); + int i; + + for (i = 0; i < 52; i += 4) + pr_info("> %16llx %16llx %16llx %16llx\n", + ptr[i + 0], ptr[i + 1], ptr[i + 2], ptr[i + 3]); + pr_info("CPU_PORT> %16llx\n", ptr[52]); +} + +void rtl838x_print_matrix(void) +{ + unsigned volatile int *ptr = RTL838X_SW_BASE + RTL838X_PORT_ISO_CTRL(0); + int i; + + if (soc_info.family == RTL8390_FAMILY_ID) + return rtl839x_print_matrix(); + + for (i = 0; i < 28; i += 8) + pr_info("> %8x %8x %8x %8x %8x %8x %8x %8x\n", + ptr[i + 0], ptr[i + 1], ptr[i + 2], ptr[i + 3], ptr[i + 4], ptr[i + 5], + ptr[i + 6], ptr[i + 7]); + pr_info("CPU_PORT> %8x\n", ptr[28]); +} + +static void rtl838x_init_stats(struct rtl838x_switch_priv *priv) +{ + mutex_lock(&priv->reg_mutex); + + /* Enable statistics module: all counters plus debug. + * On RTL839x all counters are enabled by default + */ + if (priv->family_id == RTL8380_FAMILY_ID) + sw_w32_mask(0, 3, RTL838X_STAT_CTRL); + + /* Reset statistics counters */ + sw_w32_mask(0, 1, priv->r->stat_rst); + + mutex_unlock(&priv->reg_mutex); +} + +static int rtl838x_setup(struct dsa_switch *ds) +{ + int i; + u64 port_bitmap = 0; + struct rtl838x_switch_priv *priv = ds->priv; + + pr_info("%s called\n", __func__); + + /* Disable MAC polling the PHY so that we can start configuration */ + priv->r->set_port_reg(0, priv->r->smi_poll_ctrl); + + for (i = 0; i < ds->num_ports; i++) + priv->ports[i].enable = false; + priv->ports[priv->cpu_port].enable = true; + + /* Isolate ports from each other: traffic only CPU <-> port */ + for (i = 0; i < priv->cpu_port; i++) { + if (priv->ports[i].phy) { + priv->r->set_port_iso_ctrl(1 << priv->cpu_port, i); + priv->r->mask_port_iso_ctrl(0, 1 << i, i); + port_bitmap |= 1 << i; + } + } + priv->r->set_port_iso_ctrl(port_bitmap, priv->cpu_port); + + rtl838x_print_matrix(); + + rtl838x_init_stats(priv); + + /* Enable MAC Polling PHY again */ + rtl838x_enable_phy_polling(priv); + pr_info("Please wait until PHY is settled\n"); + msleep(1000); + return 0; +} + +static void rtl838x_get_strings(struct dsa_switch *ds, + int port, u32 stringset, u8 *data) +{ + int i; + + if (stringset != ETH_SS_STATS) + return; + + for (i = 0; i < ARRAY_SIZE(rtl838x_mib); i++) + strncpy(data + i * ETH_GSTRING_LEN, rtl838x_mib[i].name, + ETH_GSTRING_LEN); +} + +static void rtl838x_get_ethtool_stats(struct dsa_switch *ds, int port, + uint64_t *data) +{ + struct rtl838x_switch_priv *priv = ds->priv; + const struct rtl838x_mib_desc *mib; + int i; + u64 high; + + for (i = 0; i < ARRAY_SIZE(rtl838x_mib); i++) { + mib = &rtl838x_mib[i]; + + data[i] = sw_r32(priv->r->stat_port_std_mib(port) + 252 - mib->offset); + if (mib->size == 2) { + high = sw_r32(priv->r->stat_port_std_mib(port) + 252 - mib->offset - 4); + data[i] |= high << 32; + } + } +} + +static int rtl838x_get_sset_count(struct dsa_switch *ds, int port, int sset) +{ + if (sset != ETH_SS_STATS) + return 0; + + return ARRAY_SIZE(rtl838x_mib); +} + +static enum dsa_tag_protocol +rtl838x_get_tag_protocol(struct dsa_switch *ds, int port) +{ + /* The switch does not tag the frames, instead internally the header + * structure for each packet is tagged accordingly. + */ + return DSA_TAG_PROTO_TRAILER; +} + +static int rtl838x_get_l2aging(struct rtl838x_switch_priv *priv) +{ + int t = sw_r32(priv->r->l2_ctrl_1) & 0x7fffff; + + pr_debug("RTL838X_L2_CTRL_1 %x\n", sw_r32(priv->r->l2_ctrl_1)); + + t = t * 128 / 625; /* Aging time in seconds. 0: L2 aging disabled */ + pr_info("L2 AGING time: %d sec\n", t); + pr_info("Dynamic aging for ports: %x\n", + sw_r32(priv->r->l2_port_aging_out)); + return t; +} + +/* + * Set Switch L2 Aging time, t is time in milliseconds + * t = 0: aging is disabled + */ +static int rtl838x_set_l2aging(struct dsa_switch *ds, u32 t) +{ + struct rtl838x_switch_priv *priv = ds->priv; + /* Convert time in mseconds to internal value */ + if (t > 0x10000000) /* Set to maximum */ + t = 0x7fffff; + else + t = ((t * 625) / 1000 + 127) / 128; + + sw_w32(t, priv->r->l2_ctrl_1); + + return 0; +} + +static void rtl838x_fast_age(struct dsa_switch *ds, int port) +{ + struct rtl838x_switch_priv *priv = ds->priv; + int s = priv->family_id == RTL8390_FAMILY_ID ? 2 : 0; + + pr_info("FAST AGE port %d\n", port); + mutex_lock(&priv->reg_mutex); + /* RTL838X_L2_TBL_FLUSH_CTRL register bits, 839x has 1 bit larger + * port fields: + * 0-4: Replacing port + * 5-9: Flushed/replaced port + * 10-21: FVID + * 22: Entry types: 1: dynamic, 0: also static + * 23: Match flush port + * 24: Match FVID + * 25: Flush (0) or replace (1) L2 entries + * 26: Status of action (1: Start, 0: Done) + */ + sw_w32(1 << (26 + s) | 1 << (23 + s) | port << 5, priv->r->l2_tbl_flush_ctrl); + + do { } while (sw_r32(priv->r->l2_tbl_flush_ctrl) & (1 << (26 + s))); + + mutex_unlock(&priv->reg_mutex); +} + +/* + * Applies the same hash algorithm as the one used currently by the ASIC + */ +static u32 rtl838x_hash(struct rtl838x_switch_priv *priv, u64 seed) +{ + u32 h1, h2, h3, h; + + if (sw_r32(priv->r->l2_ctrl_0) & 1) { + h1 = (seed >> 11) & 0x7ff; + h1 = ((h1 & 0x1f) << 6) | ((h1 >> 5) & 0x3f); + + h2 = (seed >> 33) & 0x7ff; + h2 = ((h2 & 0x3f) << 5) | ((h2 >> 6) & 0x1f); + + h3 = (seed >> 44) & 0x7ff; + h3 = ((h3 & 0x7f) << 4) | ((h3 >> 7) & 0xf); + + h = h1 ^ h2 ^ h3 ^ ((seed >> 55) & 0x1ff); + h ^= ((seed >> 22) & 0x7ff) ^ (seed & 0x7ff); + } else { + h = ((seed >> 55) & 0x1ff) ^ ((seed >> 44) & 0x7ff) + ^ ((seed >> 33) & 0x7ff) ^ ((seed >> 22) & 0x7ff) + ^ ((seed >> 11) & 0x7ff) ^ (seed & 0x7ff); + } + + return h; +} + +static u64 rtl838x_hash_key(struct rtl838x_switch_priv *priv, u64 mac, u32 vid) +{ + return rtl838x_hash(priv, mac << 12 | vid); +} + +static u64 read_l2_entry_using_hash(u32 hash, u32 position, u32 *r) +{ + u64 entry; + /* Search in SRAM, with hash and at position in hash bucket (0-3) */ + u32 idx = (0 << 14) | (hash << 2) | position; + + u32 cmd = 1 << 16 /* Execute cmd */ + | 1 << 15 /* Read */ + | 0 << 13 /* Table type 0b00 */ + | (idx & 0x1fff); + + sw_w32(cmd, RTL838X_TBL_ACCESS_L2_CTRL); + do { } while (sw_r32(RTL838X_TBL_ACCESS_L2_CTRL) & (1 << 16)); + r[0] = sw_r32(RTL838X_TBL_ACCESS_L2_DATA(0)); + r[1] = sw_r32(RTL838X_TBL_ACCESS_L2_DATA(1)); + r[2] = sw_r32(RTL838X_TBL_ACCESS_L2_DATA(2)); + + entry = (((u64) r[1]) << 32) | (r[2] & 0xfffff000) | (r[0] & 0xfff); + return entry; +} + +static u64 rtl838x_read_cam(int idx, u32 *r) +{ + u64 entry; + u32 cmd = 1 << 16 /* Execute cmd */ + | 1 << 15 /* Read */ + | 1 << 13 /* Table type 0b01 */ + | (idx & 0x3f); + sw_w32(cmd, RTL838X_TBL_ACCESS_L2_CTRL); + do { } while (sw_r32(RTL838X_TBL_ACCESS_L2_CTRL) & (1 << 16)); + r[0] = sw_r32(RTL838X_TBL_ACCESS_L2_DATA(0)); + r[1] = sw_r32(RTL838X_TBL_ACCESS_L2_DATA(1)); + r[2] = sw_r32(RTL838X_TBL_ACCESS_L2_DATA(2)); + + entry = (((u64) r[1]) << 32) | (r[2] & 0xfffff000) | (r[0] & 0xfff); + return entry; +} + +static void rtl838x_write_cam(int idx, u32 *r) +{ + u32 cmd = 1 << 16 /* Execute cmd */ + | 1 << 15 /* Read */ + | 1 << 13 /* Table type 0b01 */ + | (idx & 0x3f); + + sw_w32(r[0], RTL838X_TBL_ACCESS_L2_DATA(0)); + sw_w32(r[1], RTL838X_TBL_ACCESS_L2_DATA(1)); + sw_w32(r[2], RTL838X_TBL_ACCESS_L2_DATA(2)); + + sw_w32(cmd, RTL838X_TBL_ACCESS_L2_CTRL); + do { } while (sw_r32(RTL838X_TBL_ACCESS_L2_CTRL) & (1 << 16)); +} + +static void rtl838x_write_hash(int idx, u32 *r) +{ + u32 cmd = 1 << 16 /* Execute cmd */ + | 0 << 15 /* Write */ + | 0 << 13 /* Table type 0b00 */ + | (idx & 0x1fff); + + sw_w32(0, RTL838X_TBL_ACCESS_L2_DATA(0)); + sw_w32(0, RTL838X_TBL_ACCESS_L2_DATA(1)); + sw_w32(0, RTL838X_TBL_ACCESS_L2_DATA(2)); + sw_w32(cmd, RTL838X_TBL_ACCESS_L2_CTRL); + do { } while (sw_r32(RTL838X_TBL_ACCESS_L2_CTRL) & (1 << 16)); +} + +static void dump_fdb(struct rtl838x_switch_priv *priv) +{ + u32 r[3]; + int i; + u8 mac[6]; + u16 vid, rvid; + + mutex_lock(&priv->reg_mutex); + + for (i = 0; i < 8192; i++) { + read_l2_entry_using_hash(i >> 2, i & 0x3, r); + mac[0] = (r[1] >> 20); + mac[1] = (r[1] >> 12); + mac[2] = (r[1] >> 4); + mac[3] = (r[1] & 0xf) << 4 | (r[2] >> 28); + mac[4] = (r[2] >> 20); + mac[5] = (r[2] >> 12); + vid = r[0] & 0xfff; + rvid = r[2] & 0xfff; + + if (!(r[0] >> 17)) /* Check for invalid entry */ + continue; + + pr_info("-> port %02d: %pM, vid: %d, rvid: %d\n", + (r[0] >> 12) & priv->port_mask, &mac[0], vid, rvid); + } + + mutex_unlock(&priv->reg_mutex); +} + +static int rtl838x_port_fdb_dump(struct dsa_switch *ds, int port, + dsa_fdb_dump_cb_t *cb, void *data) +{ + u32 r[3]; + u8 mac[6]; + u16 vid, rvid; + struct rtl838x_switch_priv *priv = ds->priv; + int i; + + mutex_lock(&priv->reg_mutex); + + for (i = 0; i < 8192; i++) { + read_l2_entry_using_hash(i >> 2, i & 0x3, r); + mac[0] = (r[1] >> 20); + mac[1] = (r[1] >> 12); + mac[2] = (r[1] >> 4); + mac[3] = (r[1] & 0xf) << 4 | (r[2] >> 28); + mac[4] = (r[2] >> 20); + mac[5] = (r[2] >> 12); + vid = r[0] & 0xfff; + rvid = r[2] & 0xfff; + + if (!(r[0] >> 17)) /* Check for invalid entry */ + continue; + + if (port == ((r[0] >> 12) & 0x1f)) { + pr_info("-> mac %pM, vid: %d, rvid: %d\n", &mac[0], vid, rvid); + cb(mac, vid, (r[0] >> 19) & 1, data); + } + } + + for (i = 0; i < 64; i++) { + rtl838x_read_cam(i, r); + mac[0] = (r[1] >> 20); + mac[1] = (r[1] >> 12); + mac[2] = (r[1] >> 4); + mac[3] = (r[1] & 0xf) << 4 | (r[2] >> 28); + mac[4] = (r[2] >> 20); + mac[5] = (r[2] >> 12); + vid = r[0] & 0xfff; + + if (!(r[0] >> 17)) + continue; + + pr_info("Found in CAM: R1 %x R2 %x R3 %x\n", r[0], r[1], r[2]); + if (port == ((r[0] >> 12) & priv->port_mask)) + cb(mac, vid, (r[0] >> 19) & 1, data); + } + + mutex_unlock(&priv->reg_mutex); + return 0; +} + +static int rtl838x_port_fdb_del(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid) +{ + struct rtl838x_switch_priv *priv = ds->priv; + u64 mac = ether_addr_to_u64(addr); + u32 key = rtl838x_hash_key(priv, mac, vid); + int i; + u32 r[3]; + u64 entry; + int idx = -1; + int err = 0; + + pr_info("In %s, mac %llx, vid: %d, key: %x\n", __func__, mac, vid, key); + mutex_lock(&priv->reg_mutex); + for (i = 0; i < 4; i++) { + entry = read_l2_entry_using_hash(key, i, r); + if (!(r[0] >> 17)) /* Check for invalid entry */ + continue; + if ((entry & 0x0fffffffffffffff) == ((mac << 12) | vid)) { + idx = (key << 2) | i; + break; + } + } + + if (idx >= 0) { + r[0] = r[1] = r[2] = 0; + rtl838x_write_hash(idx, r); + goto out; + } + + /* Check CAM for spillover from hash buckets */ + for (i = 0; i < 64; i++) { + entry = rtl838x_read_cam(i, r); + if ((entry & 0x0fffffffffffffff) == ((mac << 12) | vid)) { + idx = i; + break; + } + } + if (idx >= 0) { + r[0] = r[1] = r[2] = 0; + rtl838x_write_cam(idx, r); + goto out; + } + err = -ENOENT; +out: + mutex_unlock(&priv->reg_mutex); + return err; +} + +static int rtl838x_port_fdb_add(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid) +{ + struct rtl838x_switch_priv *priv = ds->priv; + u64 mac = ether_addr_to_u64(addr); + u32 key = rtl838x_hash_key(priv, mac, vid); + int i; + u32 r[3]; + int idx = -1; + u64 entry; + int err = 0; + + mutex_lock(&priv->reg_mutex); + for (i = 0; i < 4; i++) { + entry = read_l2_entry_using_hash(key, i, r); + if (!(r[0] >> 17)) { /* Check for invalid entry */ + idx = (key << 2) | i; + break; + } + if ((entry & 0x0fffffffffffffff) == ((mac << 12) | vid)) { + idx = (key << 2) | i; + break; + } + } + if (idx >= 0) { + // Found for del: R1 60000 R2 901b0e9 R3 12b0e000, 901b0e912b0e000 + r[0] = 3 << 17 | port << 12; // Aging and port + r[0] |= vid; + r[1] = mac >> 16; + r[2] = (mac & 0xffff) << 12; /* rvid = 0 */ + rtl838x_write_hash(idx, r); + goto out; + } + + /* Hash bucket full, try CAM */ + for (i = 0; i < 64; i++) { + entry = rtl838x_read_cam(i, r); + if (!(r[0] >> 17)) { /* Check for invalid entry */ + if (idx < 0) /* First empty entry? */ + idx = i; + break; + } else if ((entry & 0x0fffffffffffffff) == ((mac << 12) | vid)) { + pr_debug("Found entry in CAM\n"); + idx = i; + break; + } + } + if (idx >= 0) { + r[0] = 3 << 17 | port << 12; // Aging + r[0] |= vid; + r[1] = mac >> 16; + r[2] = (mac & 0xffff) << 12; /* rvid = 0 */ + rtl838x_write_cam(idx, r); + goto out; + } + err = -ENOTSUPP; +out: + mutex_unlock(&priv->reg_mutex); + return err; +} + +static void rtl838x_port_stp_state_set(struct dsa_switch *ds, int port, + u8 state) +{ + u32 cmd, msti = 0; + u32 port_state[4]; + int index, bit, i; + + struct rtl838x_switch_priv *priv = ds->priv; + int n = priv->family_id == RTL8380_FAMILY_ID ? 2 : 4; + + pr_info("%s: port %d state %2x\n", __func__, port, state); + if (port >= priv->cpu_port) + return; + + mutex_lock(&priv->reg_mutex); + + index = n - (port >> 4) - 1; + bit = (port << 1) % 32; + + if (priv->family_id == RTL8380_FAMILY_ID) { + cmd = 1 << 15 /* Execute cmd */ + | 1 << 14 /* Read */ + | 2 << 12 /* Table type 0b10 */ + | (msti & 0xfff); + } else { + cmd = 1 << 16 /* Execute cmd */ + | 0 << 15 /* Read */ + | 5 << 12 /* Table type 0b101 */ + | (msti & 0xfff); + } + priv->r->exec_tbl0_cmd(cmd); + + for (i = 0; i < n; i++) + port_state[i] = sw_r32(priv->r->tbl_access_data_0(i)); + + pr_debug("Current state, port %d: %d\n", port, (port_state[index] >> bit) & 3); + port_state[index] &= ~(3 << bit); + + switch (state) { + case BR_STATE_DISABLED: /* 0 */ + port_state[index] |= (0 << bit); + break; + case BR_STATE_BLOCKING: /* 4 */ + case BR_STATE_LISTENING: /* 1 */ + port_state[index] |= (1 << bit); + break; + case BR_STATE_LEARNING: /* 2 */ + port_state[index] |= (2 << bit); + break; + case BR_STATE_FORWARDING: /* 3*/ + port_state[index] |= (3 << bit); + default: + break; + } + + if (priv->family_id == RTL8380_FAMILY_ID) { + cmd = 1 << 15 /* Execute cmd */ + | 0 << 14 /* Write */ + | 2 << 12 /* Table type 0b10 */ + | (msti & 0xfff); + } else { + cmd = 1 << 16 /* Execute cmd */ + | 1 << 15 /* Write */ + | 5 << 12 /* Table type 0b101 */ + | (msti & 0xfff); + } + for (i = 0; i < n; i++) + sw_w32(port_state[i], priv->r->tbl_access_data_0(i)); + priv->r->exec_tbl0_cmd(cmd); + + mutex_unlock(&priv->reg_mutex); +} + +static int rtl838x_port_mirror_add(struct dsa_switch *ds, int port, + struct dsa_mall_mirror_tc_entry *mirror, + bool ingress) +{ + /* We support 4 mirror groups, one destination port per group */ + int group; + struct rtl838x_switch_priv *priv = ds->priv; + + pr_info("In %s\n", __func__); + + for (group = 0; group < 4; group++) { + if (priv->mirror_group_ports[group] == mirror->to_local_port) + break; + } + if (group >= 4) { + for (group = 0; group < 4; group++) { + if (priv->mirror_group_ports[group] < 0) + break; + } + } + + if (group >= 4) + return -ENOSPC; + + pr_debug("Using group %d\n", group); + mutex_lock(&priv->reg_mutex); + /* Enable mirroring to port across VLANs (bit 11) */ + sw_w32(1 << 11 | (mirror->to_local_port << 4) | 1, RTL838X_MIR_CTRL(group)); + + if (ingress && (sw_r32(RTL838X_MIR_SPM_CTRL(group)) & (1 << port))) { + mutex_unlock(&priv->reg_mutex); + return -EEXIST; + } + if ((!ingress) && (sw_r32(RTL838X_MIR_DPM_CTRL(group)) & (1 << port))) { + mutex_unlock(&priv->reg_mutex); + return -EEXIST; + } + if (ingress) + sw_w32_mask(0, 1 << port, RTL838X_MIR_SPM_CTRL(group)); + else + sw_w32_mask(0, 1 << port, RTL838X_MIR_DPM_CTRL(group)); + + priv->mirror_group_ports[group] = mirror->to_local_port; + mutex_unlock(&priv->reg_mutex); + return 0; +} + +static void rtl838x_port_mirror_del(struct dsa_switch *ds, int port, + struct dsa_mall_mirror_tc_entry *mirror) +{ + int group = 0; + struct rtl838x_switch_priv *priv = ds->priv; + + pr_info("In %s\n", __func__); + for (group = 0; group < 4; group++) { + if (priv->mirror_group_ports[group] == mirror->to_local_port) + break; + } + if (group >= 4) + return; + + mutex_lock(&priv->reg_mutex); + if (mirror->ingress) { + /* Ingress, clear source port matrix */ + sw_w32_mask(1 << port, 0, RTL838X_MIR_SPM_CTRL(group)); + } else { + /* Egress, clear destination port matrix */ + sw_w32_mask(1 << port, 0, RTL838X_MIR_DPM_CTRL(group)); + } + + if (!(sw_r32(RTL838X_MIR_DPM_CTRL(group)) || sw_r32(RTL838X_MIR_DPM_CTRL(group)))) { + priv->mirror_group_ports[group] = -1; + sw_w32(0, RTL838X_MIR_CTRL(group)); + } + + mutex_unlock(&priv->reg_mutex); +} + + +void rtl838x_vlan_profile_dump(int index) +{ + u32 profile; + + if (index < 0 || index > 7) + return; + + profile = sw_r32(RTL838X_VLAN_PROFILE(index)); + + pr_info("VLAN %d: L2 learning: %d, L2 Unknown MultiCast Field %x", + index, profile & 1, (profile >> 1) & 0x1ff); + pr_info(" IPv4 Unkn MultiCast Field %x, IPv6 Unkn MultiCast Field: %x", + (profile >> 10) & 0x1ff, (profile >> 19) & 0x1ff); +} + +static int rtl838x_vlan_filtering(struct dsa_switch *ds, int port, + bool vlan_filtering) +{ + struct rtl838x_switch_priv *priv = ds->priv; + + pr_info("%s: port %d\n", __func__, port); + mutex_lock(&priv->reg_mutex); + + if (vlan_filtering) { + /* Enable ingress and egress filtering */ + if (port != priv->cpu_port) { + if (port < 16) { + sw_w32_mask(0b10 << (port << 1), + 0b01 << (port << 1), + RTL838X_VLAN_PORT_IGR_FLTR_0); + } else { + sw_w32_mask(0b10 << ((port - 16) << 1), + 0b01 << ((port - 16) << 1), + RTL838X_VLAN_PORT_IGR_FLTR_1); + } + } + sw_w32_mask(0, 1 << port, RTL838X_VLAN_PORT_EGR_FLTR); + } else { + /* Disable ingress and egress filtering */ + if (port != priv->cpu_port) { + if (port < 16) { + sw_w32_mask(0b11 << (port << 1), + 0, + RTL838X_VLAN_PORT_IGR_FLTR_0); + } else { + sw_w32_mask(0b11 << ((port - 16) << 1), + 0, + RTL838X_VLAN_PORT_IGR_FLTR_1); + } + } + sw_w32_mask(1 << port, 0, RTL838X_VLAN_PORT_EGR_FLTR); + } + + /* We need to do something to the CPU-Port, too */ + mutex_unlock(&priv->reg_mutex); + + return 0; +} + +static int rtl838x_vlan_prepare(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) +{ + struct rtl838x_vlan_info info; + struct rtl838x_switch_priv *priv = ds->priv; + + pr_info("%s: port %d\n", __func__, port); + + mutex_lock(&priv->reg_mutex); + + priv->r->vlan_tables_read(1, &info); + + pr_info("Tagged ports %llx, untag %llx, prof %x, MC# %d, UC# %d, MSTI %x\n", + info.tagged_ports, info.untagged_ports, info.vlan_conf & 7, + (info.vlan_conf & 8) >> 3, (info.vlan_conf & 16) >> 4, + (info.vlan_conf & 0x3e0) >> 5); + + mutex_unlock(&priv->reg_mutex); + return 0; +} + +static void rtl838x_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) +{ + struct rtl838x_vlan_info info; + struct rtl838x_switch_priv *priv = ds->priv; + int v; + u64 portmask; + + pr_info("%s port %d, vid_end %d, vid_end %d, flags %x\n", __func__, + port, vlan->vid_begin, vlan->vid_end, vlan->flags); + + if (vlan->vid_begin > 4095 || vlan->vid_end > 4095) { + dev_err(priv->dev, "VLAN out of range: %d - %d", + vlan->vid_begin, vlan->vid_end); + return; + } + + mutex_lock(&priv->reg_mutex); + + if (vlan->flags & BRIDGE_VLAN_INFO_PVID) { + for (v = vlan->vid_begin; v <= vlan->vid_end; v++) { + /* Set both inner and outer PVID of the port */ + sw_w32((v << 16) | v, RTL838X_VLAN_PORT_PB_VLAN(port)); + } + } + + if (vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED) { + for (v = vlan->vid_begin; v <= vlan->vid_end; v++) { + /* Get untagged port memberships of this vlan */ + priv->r->vlan_tables_read(v, &info); + portmask = info.untagged_ports | (1 << port); + pr_debug("Untagged ports, VLAN %d: %llx\n", v, portmask); + priv->r->vlan_set_untagged(v, portmask); + } + } else { + for (v = vlan->vid_begin; v <= vlan->vid_end; v++) { + /* Get tagged port memberships of this vlan */ + priv->r->vlan_tables_read(v, &info); + portmask = info.tagged_ports | (1 << port); + pr_debug("Tagged ports, VLAN %d: %llx\n", v, portmask); + priv->r->vlan_set_tagged(v, portmask, info.vlan_conf); + } + } + mutex_unlock(&priv->reg_mutex); +} + +static int rtl838x_vlan_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) +{ + struct rtl838x_vlan_info info; + struct rtl838x_switch_priv *priv = ds->priv; + int v; + u64 portmask; + + pr_info("%s: port %d, vid_end %d, vid_end %d, flags %x\n", __func__, + port, vlan->vid_begin, vlan->vid_end, vlan->flags); + + if (vlan->vid_begin > 4095 || vlan->vid_end > 4095) { + dev_err(priv->dev, "VLAN out of range: %d - %d", + vlan->vid_begin, vlan->vid_end); + return -ENOTSUPP; + } + + mutex_lock(&priv->reg_mutex); + + for (v = vlan->vid_begin; v <= vlan->vid_end; v++) { + /* Reset both inner and out PVID of the port */ + sw_w32(0, RTL838X_VLAN_PORT_PB_VLAN(port)); + + if (vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED) { + /* Get untagged port memberships of this vlan */ + priv->r->vlan_tables_read(v, &info); + portmask = info.untagged_ports & (~(1 << port)); + pr_info("Untagged ports, VLAN %d: %llx\n", v, portmask); + priv->r->vlan_set_untagged(v, portmask); + } + + /* Get tagged port memberships of this vlan */ + priv->r->vlan_tables_read(v, &info); + portmask = info.tagged_ports & (~(1 << port)); + pr_info("Tagged ports, VLAN %d: %llx\n", v, portmask); + priv->r->vlan_set_tagged(v, portmask, info.vlan_conf); + } + mutex_unlock(&priv->reg_mutex); + + return 0; +} + +static void rtl838x_port_bridge_leave(struct dsa_switch *ds, int port, + struct net_device *bridge) +{ + struct rtl838x_switch_priv *priv = ds->priv; + u64 port_bitmap = 1 << priv->cpu_port; + int i; + + pr_info("%s %x: %d", __func__, (u32)priv, port); + mutex_lock(&priv->reg_mutex); + for (i = 0; i < ds->num_ports; i++) { + /* Remove this port from the port matrix of the other ports + * in the same bridge. If the port is disabled, port matrix + * is kept and not being setup until the port becomes enabled. + * And the other port's port matrix cannot be broken when the + * other port is still a VLAN-aware port. + */ + if (dsa_is_user_port(ds, i) && i != port) { + if (dsa_to_port(ds, i)->bridge_dev != bridge) + continue; + if (priv->ports[i].enable) + priv->r->mask_port_iso_ctrl(1 << port, 0, i); + priv->ports[i].pm |= 1 << port; + + port_bitmap &= ~(1 << i); + } + } + + /* Add all other ports to this port matrix. */ + if (priv->ports[port].enable) + priv->r->mask_port_iso_ctrl(0, port_bitmap, port); + priv->ports[port].pm &= ~port_bitmap; + mutex_unlock(&priv->reg_mutex); + + rtl838x_print_matrix(); +} + +static int rtl838x_port_bridge_join(struct dsa_switch *ds, int port, + struct net_device *bridge) +{ + struct rtl838x_switch_priv *priv = ds->priv; + u64 port_bitmap = 1 << priv->cpu_port; + int i; + + pr_info("%s %x: %d %llx", __func__, (u32)priv, port, port_bitmap); + mutex_lock(&priv->reg_mutex); + for (i = 0; i < ds->num_ports; i++) { + /* Add this port to the port matrix of the other ports in the + * same bridge. If the port is disabled, port matrix is kept + * and not being setup until the port becomes enabled. + */ + if (dsa_is_user_port(ds, i) && i != port) { + if (dsa_to_port(ds, i)->bridge_dev != bridge) + continue; + if (priv->ports[i].enable) + priv->r->mask_port_iso_ctrl(0, 1 << port, i); + priv->ports[i].pm |= 1 << port; + + port_bitmap |= 1 << i; + } + } + + /* Add all other ports to this port matrix. */ + if (priv->ports[port].enable) { + priv->r->mask_port_iso_ctrl(0, 1 << port, priv->cpu_port); + priv->r->mask_port_iso_ctrl(0, port_bitmap, port); + } + priv->ports[port].pm |= port_bitmap; + mutex_unlock(&priv->reg_mutex); + + return 0; +} + +static int rtl838x_port_enable(struct dsa_switch *ds, int port, + struct phy_device *phydev) +{ + struct rtl838x_switch_priv *priv = ds->priv; + + pr_info("%s: %x %d", __func__, (u32) priv, port); + priv->ports[port].enable = true; + + if (dsa_is_cpu_port(ds, port)) + return 0; + + /* add port to switch mask of CPU_PORT */ + priv->r->mask_port_iso_ctrl(0, 1 << port, priv->cpu_port); + + /* add all other ports in the same bridge to switch mask of port */ + priv->r->mask_port_iso_ctrl(0, priv->ports[port].pm, port); + + /* enable PHY polling */ + sw_w32_mask(0, 1 << port, RTL838X_SMI_POLL_CTRL); + + return 0; +} + +static void rtl838x_port_disable(struct dsa_switch *ds, int port) +{ + struct rtl838x_switch_priv *priv = ds->priv; + + pr_info("%s %x: %d", __func__, (u32)priv, port); + + /* you can only disable user ports */ + if (!dsa_is_user_port(ds, port)) + return; + + /* remove port from switch mask of CPU_PORT */ + priv->r->mask_port_iso_ctrl(1 << port, 0, priv->cpu_port); + + /* remove all other ports in the same bridge from switch mask of port */ + priv->r->mask_port_iso_ctrl(priv->ports[port].pm, 0, port); + + priv->ports[port].enable = false; + + /* disable PHY polling */ + sw_w32_mask(1 << port, 0, RTL838X_SMI_POLL_CTRL); +} + +static int rtl838x_get_mac_eee(struct dsa_switch *ds, int port, + struct ethtool_eee *e) +{ + struct rtl838x_switch_priv *priv = ds->priv; + + pr_info("%s: port %d", __func__, port); + e->supported = SUPPORTED_100baseT_Full | SUPPORTED_1000baseT_Full; + if (sw_r32(priv->r->mac_force_mode_ctrl(port)) & (1 << 9)) + e->advertised |= ADVERTISED_100baseT_Full; + + if (sw_r32(priv->r->mac_force_mode_ctrl(port)) & (1 << 10)) + e->advertised |= ADVERTISED_1000baseT_Full; + + e->eee_enabled = priv->ports[port].eee_enabled; + pr_info("enabled: %d, active %x\n", e->eee_enabled, e->advertised); + + if (sw_r32(RTL838X_MAC_EEE_ABLTY) & (1 << port)) { + e->lp_advertised = ADVERTISED_100baseT_Full; + e->lp_advertised |= ADVERTISED_1000baseT_Full; + } + + e->eee_active = !!(e->advertised & e->lp_advertised); + pr_info("active: %d, lp %x\n", e->eee_active, e->lp_advertised); + + return 0; +} + +static int rtl838x_set_mac_eee(struct dsa_switch *ds, int port, + struct ethtool_eee *e) +{ + struct rtl838x_switch_priv *priv = ds->priv; + + pr_info("%s: port %d", __func__, port); + if (e->eee_enabled) { + pr_info("Globally enabling EEE\n"); + sw_w32_mask(0x4, 0, RTL838X_SMI_GLB_CTRL); + } + if (e->eee_enabled) { + pr_info("Enabling EEE for MAC %d\n", port); + sw_w32_mask(0, 3 << 9, priv->r->mac_force_mode_ctrl(port)); + sw_w32_mask(0, 1 << port, RTL838X_EEE_PORT_TX_EN); + sw_w32_mask(0, 1 << port, RTL838X_EEE_PORT_RX_EN); + priv->ports[port].eee_enabled = true; + e->eee_enabled = true; + } else { + pr_info("Disabling EEE for MAC %d\n", port); + sw_w32_mask(3 << 9, 0, priv->r->mac_force_mode_ctrl(port)); + sw_w32_mask(1 << port, 0, RTL838X_EEE_PORT_TX_EN); + sw_w32_mask(1 << port, 0, RTL838X_EEE_PORT_RX_EN); + priv->ports[port].eee_enabled = false; + e->eee_enabled = false; + } + return 0; +} + +static void rtl838x_phylink_mac_config(struct dsa_switch *ds, int port, + unsigned int mode, + const struct phylink_link_state *state) +{ + struct rtl838x_switch_priv *priv = ds->priv; + u32 reg; + + pr_info("%s port %d, mode %x\n", __func__, port, mode); + + if (port == priv->cpu_port) { + /* Set Speed, duplex, flow control + * FORCE_EN | LINK_EN | NWAY_EN | DUP_SEL + * | SPD_SEL = 0b10 | FORCE_FC_EN | PHY_MASTER_SLV_MANUAL_EN + * | MEDIA_SEL + */ + if (priv->family_id == RTL8380_FAMILY_ID) { + sw_w32(0x6192F, priv->r->mac_force_mode_ctrl(priv->cpu_port)); + /* allow CRC errors on CPU-port */ + sw_w32_mask(0, 0x8, RTL838X_MAC_PORT_CTRL(priv->cpu_port)); + } else { + sw_w32_mask(0, 3, priv->r->mac_force_mode_ctrl(priv->cpu_port)); + } + return; + } + + reg = sw_r32(priv->r->mac_force_mode_ctrl(port)); + if (mode == MLO_AN_PHY) { + pr_info("PHY autonegotiates\n"); + reg |= 1 << 2; + sw_w32(reg, priv->r->mac_force_mode_ctrl(port)); + return; + } + + if (mode != MLO_AN_FIXED) + pr_info("Not fixed\n"); + + /* Clear id_mode_dis bit, and the existing port mode, let + * RGMII_MODE_EN bet set by mac_link_{up,down} + */ + reg &= ~(RX_PAUSE_EN | TX_PAUSE_EN); + + if (state->pause & MLO_PAUSE_TXRX_MASK) { + if (state->pause & MLO_PAUSE_TX) + reg |= TX_PAUSE_EN; + reg |= RX_PAUSE_EN; + } + + reg &= ~(3 << 4); + switch (state->speed) { + case SPEED_1000: + reg |= 2 << 4; + break; + case SPEED_100: + reg |= 1 << 4; + break; + } + + reg &= ~(DUPLEX_FULL | FORCE_LINK_EN); + if (state->link) + reg |= FORCE_LINK_EN; + if (state->duplex == DUPLEX_FULL) + reg |= DUPLX_MODE; + + // Disable AN + reg &= ~(1 << 2); + sw_w32(reg, priv->r->mac_force_mode_ctrl(port)); +} + +static void rtl838x_phylink_mac_link_down(struct dsa_switch *ds, int port, + unsigned int mode, + phy_interface_t interface) +{ + /* Stop TX/RX to port */ + sw_w32_mask(0x03, 0, RTL838X_MAC_PORT_CTRL(port)); +} + +static void rtl838x_phylink_mac_link_up(struct dsa_switch *ds, int port, + unsigned int mode, + phy_interface_t interface, + struct phy_device *phydev) +{ + /* Restart TX/RX to port */ + sw_w32_mask(0, 0x03, RTL838X_MAC_PORT_CTRL(port)); +} + +static void rtl838x_phylink_validate(struct dsa_switch *ds, int port, + unsigned long *supported, + struct phylink_link_state *state) +{ + __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; + + pr_info("In %s port %d", __func__, port); + + if (!phy_interface_mode_is_rgmii(state->interface) && + state->interface != PHY_INTERFACE_MODE_1000BASEX && + state->interface != PHY_INTERFACE_MODE_MII && + state->interface != PHY_INTERFACE_MODE_REVMII && + state->interface != PHY_INTERFACE_MODE_GMII && + state->interface != PHY_INTERFACE_MODE_QSGMII && + state->interface != PHY_INTERFACE_MODE_INTERNAL && + state->interface != PHY_INTERFACE_MODE_SGMII) { + bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); + dev_err(ds->dev, + "Unsupported interface: %d for port %d\n", + state->interface, port); + return; + } + + /* switch chip-id? if (priv->id == 0x8382) */ + + /* Allow all the expected bits */ + phylink_set(mask, Autoneg); + phylink_set_port_modes(mask); + phylink_set(mask, Pause); + phylink_set(mask, Asym_Pause); + + /* With the exclusion of MII and Reverse MII, we support Gigabit, + * including Half duplex + */ + if (state->interface != PHY_INTERFACE_MODE_MII && + state->interface != PHY_INTERFACE_MODE_REVMII) { + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseT_Half); + } + + /* On both the 8380 and 8382, ports 24-27 are SFP ports */ + if (port >= 24 && port <= 27) + phylink_set(mask, 1000baseX_Full); + + phylink_set(mask, 10baseT_Half); + phylink_set(mask, 10baseT_Full); + phylink_set(mask, 100baseT_Half); + phylink_set(mask, 100baseT_Full); + + bitmap_and(supported, supported, mask, + __ETHTOOL_LINK_MODE_MASK_NBITS); + bitmap_and(state->advertising, state->advertising, mask, + __ETHTOOL_LINK_MODE_MASK_NBITS); +} + +static int rtl838x_phylink_mac_link_state(struct dsa_switch *ds, int port, + struct phylink_link_state *state) +{ + struct rtl838x_switch_priv *priv = ds->priv; + u32 speed; + + if (port < 0 || port > priv->cpu_port) + return -EINVAL; + + state->link = 0; + if (sw_r32(RTL838X_MAC_LINK_STS) & (1 << port)) + state->link = 1; + state->duplex = 0; + if (sw_r32(RTL838X_MAC_LINK_DUP_STS) & (1 << port)) + state->duplex = 1; + + speed = sw_r32(RTL838X_MAC_LINK_SPD_STS(port)); + speed >>= (port % 16) << 1; + switch (speed & 0x3) { + case 0: + state->speed = SPEED_10; + break; + case 1: + state->speed = SPEED_100; + break; + case 2: + state->speed = SPEED_1000; + break; + case 3: + if (port == 24 || port == 26) /* Internal serdes */ + state->speed = SPEED_2500; + else + state->speed = SPEED_100; /* Is in fact 500Mbit */ + } + + state->pause &= (MLO_PAUSE_RX | MLO_PAUSE_TX); + if (sw_r32(RTL838X_MAC_RX_PAUSE_STS) & (1 << port)) + state->pause |= MLO_PAUSE_RX; + if (sw_r32(RTL838X_MAC_TX_PAUSE_STS) & (1 << port)) + state->pause |= MLO_PAUSE_TX; + return 1; +} + +static int rtl838x_mdio_probe(struct rtl838x_switch_priv *priv) +{ + struct device *dev = priv->dev; + struct device_node *dn, *mii_np = dev->of_node; + struct mii_bus *bus; + int ret; + u32 pn; + + pr_info("In %s\n", __func__); + mii_np = of_find_compatible_node(NULL, NULL, "realtek,rtl838x-mdio"); + if (mii_np) { + pr_info("Found compatible MDIO node!\n"); + } else { + dev_err(priv->dev, "no %s child node found", "mdio-bus"); + return -ENODEV; + } + + priv->mii_bus = of_mdio_find_bus(mii_np); + if (!priv->mii_bus) { + pr_info("Deferring probe of mdio bus\n"); + return -EPROBE_DEFER; + } + if (!of_device_is_available(mii_np)) + ret = -ENODEV; + + bus = devm_mdiobus_alloc(priv->ds->dev); + if (!bus) + return -ENOMEM; + + bus->name = "rtl838x slave mii"; + bus->read = &rtl838x_mdio_read; + bus->write = &rtl838x_mdio_write; + snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", bus->name, dev->id); + bus->parent = dev; + priv->ds->slave_mii_bus = bus; + priv->ds->slave_mii_bus->priv = priv; + + ret = mdiobus_register(priv->ds->slave_mii_bus); + if (ret && mii_np) { + of_node_put(dn); + return ret; + } + + dn = mii_np; + for_each_node_by_name(dn, "ethernet-phy") { + if (of_property_read_u32(dn, "reg", &pn)) + continue; + + // Check for the integrated SerDes of the RTL8380M first + if (of_property_read_bool(dn, "phy-is-integrated") + && priv->id == 0x8380 && pn >= 24) { + pr_info("----> FĂ“UND A SERDES\n"); + priv->ports[pn].phy = PHY_RTL838X_SDS; + continue; + } + + if (of_property_read_bool(dn, "phy-is-integrated") + && !of_property_read_bool(dn, "sfp")) { + priv->ports[pn].phy = PHY_RTL8218B_INT; + continue; + } + + if (!of_property_read_bool(dn, "phy-is-integrated") + && of_property_read_bool(dn, "sfp")) { + priv->ports[pn].phy = PHY_RTL8214FC; + continue; + } + + if (!of_property_read_bool(dn, "phy-is-integrated") + && !of_property_read_bool(dn, "sfp")) { + priv->ports[pn].phy = PHY_RTL8218B_EXT; + continue; + } + } + + /* Disable MAC polling the PHY so that we can start configuration */ + sw_w32(0x00000000, RTL838X_SMI_POLL_CTRL); + + /* Enable PHY control via SoC */ + sw_w32_mask(0, 1 << 15, RTL838X_SMI_GLB_CTRL); + /* Power on fibre ports and reset them if necessary */ + if (priv->ports[24].phy == PHY_RTL838X_SDS) { + pr_info("Powering on fibre ports & reset\n"); + rtl8380_sds_power(24, 1); + rtl8380_sds_power(26, 1); + } + + pr_info("%s done\n", __func__); + return 0; +} + +static const struct dsa_switch_ops rtl838x_switch_ops = { + .get_tag_protocol = rtl838x_get_tag_protocol, + .setup = rtl838x_setup, + .port_vlan_filtering = rtl838x_vlan_filtering, + .port_vlan_prepare = rtl838x_vlan_prepare, + .port_vlan_add = rtl838x_vlan_add, + .port_vlan_del = rtl838x_vlan_del, + .port_bridge_join = rtl838x_port_bridge_join, + .port_bridge_leave = rtl838x_port_bridge_leave, + .port_stp_state_set = rtl838x_port_stp_state_set, + .set_ageing_time = rtl838x_set_l2aging, + .port_fast_age = rtl838x_fast_age, + .port_fdb_add = rtl838x_port_fdb_add, + .port_fdb_del = rtl838x_port_fdb_del, + .port_fdb_dump = rtl838x_port_fdb_dump, + .port_enable = rtl838x_port_enable, + .port_disable = rtl838x_port_disable, + .port_mirror_add = rtl838x_port_mirror_add, + .port_mirror_del = rtl838x_port_mirror_del, + .phy_read = dsa_phy_read, + .phy_write = dsa_phy_write, + .get_strings = rtl838x_get_strings, + .get_ethtool_stats = rtl838x_get_ethtool_stats, + .get_sset_count = rtl838x_get_sset_count, + .phylink_validate = rtl838x_phylink_validate, + .phylink_mac_link_state = rtl838x_phylink_mac_link_state, + .phylink_mac_config = rtl838x_phylink_mac_config, + .phylink_mac_link_down = rtl838x_phylink_mac_link_down, + .phylink_mac_link_up = rtl838x_phylink_mac_link_up, + .set_mac_eee = rtl838x_set_mac_eee, + .get_mac_eee = rtl838x_get_mac_eee, + +}; + +static int __init rtl838x_sw_probe(struct platform_device *pdev) +{ + int err = 0, i; + struct rtl838x_switch_priv *priv; + struct device *dev = &pdev->dev; + + pr_info("Probing RTL838X switch device\n"); + if (!pdev->dev.of_node) { + dev_err(dev, "No DT found\n"); + return -EINVAL; + } + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->ds = dsa_switch_alloc(dev, DSA_MAX_PORTS); + + if (!priv->ds) + return -ENOMEM; + priv->ds->dev = dev; + priv->ds->priv = priv; + priv->ds->ops = &rtl838x_switch_ops; + priv->dev = dev; + + priv->family_id = soc_info.family; + priv->id = soc_info.id; + if (soc_info.family == RTL8380_FAMILY_ID) { + priv->cpu_port = RTL838X_CPU_PORT; + priv->port_mask = 0x1f; + priv->r = &rtl838x_reg; + priv->ds->num_ports = 30; + rtl8380_get_version(priv); + } else { + priv->cpu_port = RTL839X_CPU_PORT; + priv->port_mask = 0x3f; + priv->r = &rtl839x_reg; + priv->ds->num_ports = 53; + rtl8390_get_version(priv); + } + pr_info("Chip version %c\n", priv->version); + + err = rtl838x_mdio_probe(priv); + if (err) { + /* Probing fails the 1st time because of missing ethernet driver + * initialization. Use this to disable traffic in case the bootloader left if on + */ + return err; + } + err = dsa_register_switch(priv->ds); + if (err) { + dev_err(dev, "Error registering switch: %d\n", err); + return err; + } + + /* Enable link and media change interrupts. Are the SERDES masks needed? */ + sw_w32_mask(0, 3, priv->r->isr_glb_src); + /* ... for all ports */ + priv->r->set_port_reg(0xffffffffffffffff, priv->r->isr_port_link_sts_chg); + priv->r->set_port_reg(0xffffffffffffffff, priv->r->imr_port_link_sts_chg); + + priv->link_state_irq = 20; + if (priv->family_id == RTL8380_FAMILY_ID) { + err = request_irq(priv->link_state_irq, rtl838x_switch_irq, + IRQF_SHARED, "rtl8838x-link-state", priv->ds); + } else { + err = request_irq(priv->link_state_irq, rtl839x_switch_irq, + IRQF_SHARED, "rtl8838x-link-state", priv->ds); + } + if (err) { + dev_err(dev, "Error setting up switch interrupt.\n"); + /* Need to free allocated switch here */ + } + + /* Enable interrupts for switch */ + sw_w32(0x1, priv->r->imr_glb); + + rtl838x_get_l2aging(priv); + + /* Clear all destination ports for mirror groups */ + for (i = 0; i < 4; i++) + priv->mirror_group_ports[i] = -1; + + return err; +} + +static int rtl838x_sw_remove(struct platform_device *pdev) +{ + pr_info("Removing platform driver for rtl838x-sw\n"); + return 0; +} + +static const struct of_device_id rtl838x_switch_of_ids[] = { + { .compatible = "realtek,rtl838x-switch"}, + { /* sentinel */ } +}; + + +MODULE_DEVICE_TABLE(of, rtl838x_switch_of_ids); + +static struct platform_driver rtl838x_switch_driver = { + .probe = rtl838x_sw_probe, + .remove = rtl838x_sw_remove, + .driver = { + .name = "rtl838x-switch", + .pm = NULL, + .of_match_table = rtl838x_switch_of_ids, + }, +}; + +module_platform_driver(rtl838x_switch_driver); + +MODULE_AUTHOR("B. Koblitz"); +MODULE_DESCRIPTION("RTL838X SoC Switch Driver"); +MODULE_LICENSE("GPL"); diff --git a/target/linux/rtl838x/files-5.4/drivers/net/ethernet/rtl838x_eth.c b/target/linux/rtl838x/files-5.4/drivers/net/ethernet/rtl838x_eth.c new file mode 100644 index 0000000000..9634b6a24f --- /dev/null +++ b/target/linux/rtl838x/files-5.4/drivers/net/ethernet/rtl838x_eth.c @@ -0,0 +1,1205 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/drivers/net/ethernet/rtl838x_eth.c + * Copyright (C) 2020 B. Koblitz + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "rtl838x_eth.h" + +extern struct rtl838x_soc_info soc_info; + +/* + * Maximum number of RX rings is 8, assigned by switch based on + * packet/port priortity (not implemented) + * Maximum number of TX rings is 2 (only ring 0 used) + * RX ringlength needs to be at least 200, otherwise CPU and Switch + * may gridlock. + */ +#define RXRINGS 2 +#define RXRINGLEN 300 +#define TXRINGS 2 +#define TXRINGLEN 160 +#define TX_EN 0x8 +#define RX_EN 0x4 +#define TX_DO 0x2 +#define WRAP 0x2 + +#define RING_BUFFER 1600 + +struct p_hdr { + uint8_t *buf; + uint16_t reserved; + uint16_t size; /* buffer size */ + uint16_t offset; + uint16_t len; /* pkt len */ + uint16_t reserved2; + uint16_t cpu_tag[5]; +} __packed __aligned(1); + +struct ring_b { + uint32_t rx_r[RXRINGS][RXRINGLEN]; + uint32_t tx_r[TXRINGS][TXRINGLEN]; + struct p_hdr rx_header[RXRINGS][RXRINGLEN]; + struct p_hdr tx_header[TXRINGS][TXRINGLEN]; + uint32_t c_rx[RXRINGS]; + uint32_t c_tx[TXRINGS]; + uint8_t rx_space[RXRINGS*RXRINGLEN*RING_BUFFER]; + uint8_t tx_space[TXRINGLEN*RING_BUFFER]; +}; + +struct rtl838x_eth_priv { + struct net_device *netdev; + struct platform_device *pdev; + void *membase; + spinlock_t lock; + struct mii_bus *mii_bus; + struct napi_struct napi; + struct phylink *phylink; + struct phylink_config phylink_config; + u16 id; + u16 family_id; + const struct rtl838x_reg *r; + u8 cpu_port; +}; + +static const struct rtl838x_reg rtl838x_reg = { + .mac_port_ctrl = rtl838x_mac_port_ctrl, + .dma_if_intr_sts = RTL838X_DMA_IF_INTR_STS, + .dma_if_intr_msk = RTL838X_DMA_IF_INTR_MSK, + .dma_if_ctrl = RTL838X_DMA_IF_CTRL, + .mac_force_mode_ctrl = rtl838x_mac_force_mode_ctrl, + .dma_rx_base = rtl838x_dma_rx_base, + .dma_tx_base = rtl838x_dma_tx_base, + .dma_if_rx_ring_size = rtl838x_dma_if_rx_ring_size, + .dma_if_rx_ring_cntr = rtl838x_dma_if_rx_ring_cntr, + .dma_if_rx_cur = rtl838x_dma_if_rx_cur, + .rst_glb_ctrl = RTL838X_RST_GLB_CTRL_0, + .get_mac_link_sts = rtl838x_get_mac_link_sts, + .get_mac_link_dup_sts = rtl838x_get_mac_link_dup_sts, + .get_mac_link_spd_sts = rtl838x_get_mac_link_spd_sts, + .get_mac_rx_pause_sts = rtl838x_get_mac_rx_pause_sts, + .get_mac_tx_pause_sts = rtl838x_get_mac_tx_pause_sts, + .mac = RTL838X_MAC, + .l2_tbl_flush_ctrl = RTL838X_L2_TBL_FLUSH_CTRL, +}; + +static const struct rtl838x_reg rtl839x_reg = { + .mac_port_ctrl = rtl839x_mac_port_ctrl, + .dma_if_intr_sts = RTL839X_DMA_IF_INTR_STS, + .dma_if_intr_msk = RTL839X_DMA_IF_INTR_MSK, + .dma_if_ctrl = RTL839X_DMA_IF_CTRL, + .mac_force_mode_ctrl = rtl839x_mac_force_mode_ctrl, + .dma_rx_base = rtl839x_dma_rx_base, + .dma_tx_base = rtl839x_dma_tx_base, + .dma_if_rx_ring_size = rtl839x_dma_if_rx_ring_size, + .dma_if_rx_ring_cntr = rtl839x_dma_if_rx_ring_cntr, + .dma_if_rx_cur = rtl839x_dma_if_rx_cur, + .rst_glb_ctrl = RTL839X_RST_GLB_CTRL, + .get_mac_link_sts = rtl839x_get_mac_link_sts, + .get_mac_link_dup_sts = rtl839x_get_mac_link_dup_sts, + .get_mac_link_spd_sts = rtl839x_get_mac_link_spd_sts, + .get_mac_rx_pause_sts = rtl839x_get_mac_rx_pause_sts, + .get_mac_tx_pause_sts = rtl839x_get_mac_tx_pause_sts, + .mac = RTL839X_MAC, + .l2_tbl_flush_ctrl = RTL839X_L2_TBL_FLUSH_CTRL, +}; + +/* + * Discard the RX ring-buffers, called as part of the net-ISR + * when the buffer runs over + * Caller needs to hold priv->lock + */ +static void rtl838x_rb_cleanup(struct rtl838x_eth_priv *priv) +{ + int r; + u32 *last; + struct p_hdr *h; + struct ring_b *ring = priv->membase; + + for (r = 0; r < RXRINGS; r++) { + last = (u32 *)KSEG1ADDR(sw_r32(priv->r->dma_if_rx_cur(r))); + do { + if ((ring->rx_r[r][ring->c_rx[r]] & 0x1)) + break; + h = &ring->rx_header[r][ring->c_rx[r]]; + h->buf = (u8 *)CPHYSADDR(ring->rx_space + + r * ring->c_rx[r] * RING_BUFFER); + h->size = RING_BUFFER; + h->len = 0; + /* make sure the header is visible to the ASIC */ + mb(); + + ring->rx_r[r][ring->c_rx[r]] = CPHYSADDR(h) | 0x1 + | (ring->c_rx[r] == (RXRINGLEN-1) ? WRAP : 0x1); + ring->c_rx[r] = (ring->c_rx[r] + 1) % RXRINGLEN; + } while (&ring->rx_r[r][ring->c_rx[r]] != last); + } +} + +static irqreturn_t rtl838x_net_irq(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct rtl838x_eth_priv *priv = netdev_priv(dev); + u32 status = sw_r32(priv->r->dma_if_intr_sts); + + spin_lock(&priv->lock); + /* Ignore TX interrupt */ + if ((status & 0xf0000)) { + /* Clear ISR */ + sw_w32(0x000f0000, priv->r->dma_if_intr_sts); + } + + /* RX interrupt */ + if (status & 0x0ff00) { + /* Disable RX interrupt */ + sw_w32_mask(0xff00, 0, priv->r->dma_if_intr_msk); + sw_w32(0x0000ff00, priv->r->dma_if_intr_sts); + napi_schedule(&priv->napi); + } + + /* RX buffer overrun */ + if (status & 0x000ff) { + sw_w32(0x000000ff, priv->r->dma_if_intr_sts); + rtl838x_rb_cleanup(priv); + } + + spin_unlock(&priv->lock); + return IRQ_HANDLED; +} + +static void rtl838x_hw_reset(struct rtl838x_eth_priv *priv) +{ + u32 int_saved; + + pr_info("RESETTING %x, CPU_PORT %d\n", priv->family_id, priv->cpu_port); + /* Stop TX/RX */ + sw_w32(0x0, priv->r->mac_port_ctrl(priv->cpu_port)); + mdelay(500); + + int_saved = sw_r32(priv->r->dma_if_intr_msk); + /* Reset NIC */ + sw_w32(0x08, priv->r->rst_glb_ctrl); + do { + udelay(20); + } while (sw_r32(priv->r->rst_glb_ctrl) & 0x08); + mdelay(100); + + /* Restore notification settings: on RTL838x these bits are null */ + sw_w32_mask(7 << 20, int_saved & (7 << 20), priv->r->dma_if_intr_msk); + + /* Restart TX/RX to CPU port */ + sw_w32(0x03, priv->r->mac_port_ctrl(priv->cpu_port)); + + if (priv->family_id == RTL8380_FAMILY_ID) { + /* Set Speed, duplex, flow control + * FORCE_EN | LINK_EN | NWAY_EN | DUP_SEL + * | SPD_SEL = 0b10 | FORCE_FC_EN | PHY_MASTER_SLV_MANUAL_EN + * | MEDIA_SEL + */ + sw_w32(0x6192F, priv->r->mac_force_mode_ctrl(priv->cpu_port)); + /* allow CRC errors on CPU-port */ + sw_w32_mask(0, 0x8, priv->r->mac_port_ctrl(priv->cpu_port)); + } else { + /* Force CPU port link up */ + sw_w32_mask(0, 3, priv->r->mac_force_mode_ctrl(priv->cpu_port)); + } + + /* Disable and clear interrupts */ + sw_w32(0x00000000, priv->r->dma_if_intr_msk); + sw_w32(0xffffffff, priv->r->dma_if_intr_sts); +} + +static void rtl838x_hw_ring_setup(struct rtl838x_eth_priv *priv) +{ + int i; + struct ring_b *ring = priv->membase; + + for (i = 0; i < RXRINGS; i++) + sw_w32(CPHYSADDR(&ring->rx_r[i]), priv->r->dma_rx_base(i)); + + for (i = 0; i < TXRINGS; i++) + sw_w32(CPHYSADDR(&ring->tx_r[i]), priv->r->dma_tx_base(i)); +} + +static void rtl838x_hw_en_rxtx(struct rtl838x_eth_priv *priv) +{ + u32 v; + + pr_info("%s\n", __func__); + /* Disable Head of Line features for all RX rings */ + sw_w32(0xffffffff, priv->r->dma_if_rx_ring_size(0)); + + /* Truncate RX buffer to 0x640 (1600) bytes, pad TX */ + sw_w32(0x06400020, priv->r->dma_if_ctrl); + + /* Enable RX done, RX overflow and TX done interrupts */ + sw_w32(0xfffff, priv->r->dma_if_intr_msk); + + /* Enable traffic, engine expects empty FCS field */ + sw_w32_mask(0, RX_EN | TX_EN, priv->r->dma_if_ctrl); + + /* Make sure to flood all traffic to CPU_PORT */ + if (priv->family_id == RTL8390_FAMILY_ID) { + /* CPU port joins Lookup Miss Flooding Portmask */ + /* Table access: CMD: read, table = 2 */ + /* Sets MC_PMSK table port bit for port 52 to 1 */ + sw_w32(0x28000, RTL839X_TBL_ACCESS_L2_CTRL); + do { } while (sw_r32(RTL839X_TBL_ACCESS_L2_CTRL) & (1 << 17)); + v = sw_r32(RTL839X_TBL_ACCESS_L2_DATA(0)); + sw_w32(v | 0x80000000, RTL839X_TBL_ACCESS_L2_DATA(0)); + sw_w32(0x38000, RTL839X_TBL_ACCESS_L2_CTRL); + do { } while (sw_r32(RTL839X_TBL_ACCESS_L2_CTRL) & (1 << 17)); + } +} + +static void rtl838x_setup_ring_buffer(struct ring_b *ring) +{ + int i, j; + + struct p_hdr *h; + + for (i = 0; i < RXRINGS; i++) { + for (j = 0; j < RXRINGLEN; j++) { + h = &ring->rx_header[i][j]; + h->buf = (u8 *)CPHYSADDR(ring->rx_space + i * j * RING_BUFFER); + h->reserved = 0; + h->size = RING_BUFFER; + h->offset = 0; + h->len = 0; + /* All rings owned by switch, last one wraps */ + ring->rx_r[i][j] = CPHYSADDR(h) | 1 | (j == (RXRINGLEN - 1) ? WRAP : 0); + } + ring->c_rx[i] = 0; + } + + for (i = 0; i < TXRINGS; i++) { + for (j = 0; j < TXRINGLEN; j++) { + h = &ring->tx_header[i][j]; + h->buf = (u8 *)CPHYSADDR(ring->tx_space + i * j * RING_BUFFER); + h->reserved = 0; + h->size = RING_BUFFER; + h->offset = 0; + h->len = 0; + ring->tx_r[i][j] = CPHYSADDR(&ring->tx_header[i][j]); + } + /* Last header is wrapping around */ + ring->tx_r[i][j-1] |= 2; + ring->c_tx[i] = 0; + } +} + +static int rtl838x_eth_open(struct net_device *ndev) +{ + unsigned long flags; + struct rtl838x_eth_priv *priv = netdev_priv(ndev); + struct ring_b *ring = priv->membase; + int err; + + pr_info("%s called %x, ring %x\n", __func__, (uint32_t)priv, (uint32_t)ring); + spin_lock_irqsave(&priv->lock, flags); + rtl838x_hw_reset(priv); + rtl838x_setup_ring_buffer(ring); + rtl838x_hw_ring_setup(priv); + err = request_irq(ndev->irq, rtl838x_net_irq, IRQF_SHARED, + ndev->name, ndev); + if (err) { + netdev_err(ndev, "%s: could not acquire interrupt: %d\n", + __func__, err); + return err; + } + phylink_start(priv->phylink); + + napi_enable(&priv->napi); + netif_start_queue(ndev); + + rtl838x_hw_en_rxtx(priv); + + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static void rtl838x_hw_stop(struct rtl838x_eth_priv *priv) +{ + int i; + + /* Block all ports */ + if (priv->family_id == RTL8380_FAMILY_ID) { + sw_w32(0x03000000, RTL838X_TBL_ACCESS_DATA_0(0)); + sw_w32(0x00000000, RTL838X_TBL_ACCESS_DATA_0(1)); + sw_w32(1 << 15 | 2 << 12, RTL838X_TBL_ACCESS_CTRL_0); + } + + /* Flush L2 address cache */ + if (priv->family_id == RTL8380_FAMILY_ID) { + for (i = 0; i <= priv->cpu_port; i++) { + sw_w32(1 << 26 | 1 << 23 | i << 5, priv->r->l2_tbl_flush_ctrl); + do { } while (sw_r32(priv->r->l2_tbl_flush_ctrl) & (1 << 26)); + } + } else { + for (i = 0; i <= priv->cpu_port; i++) { + sw_w32(1 << 28 | 1 << 25 | i << 5, priv->r->l2_tbl_flush_ctrl); + do { } while (sw_r32(priv->r->l2_tbl_flush_ctrl) & (1 << 28)); + } + } + + /* CPU-Port: Link down BUG: Works only for RTL838x */ + sw_w32(0x6192D, priv->r->mac_force_mode_ctrl(priv->cpu_port)); + mdelay(100); + + /* Disable traffic */ + sw_w32_mask(RX_EN | TX_EN, 0, priv->r->dma_if_ctrl); + mdelay(200); + + /* Disable all TX/RX interrupts */ + sw_w32(0x00000000, priv->r->dma_if_intr_msk); + sw_w32(0x000fffff, priv->r->dma_if_intr_sts); + sw_w32(0x00000000, priv->r->dma_if_ctrl); + mdelay(200); +} + +static int rtl838x_eth_stop(struct net_device *ndev) +{ + unsigned long flags; + struct rtl838x_eth_priv *priv = netdev_priv(ndev); + + pr_info("in %s\n", __func__); + + spin_lock_irqsave(&priv->lock, flags); + phylink_stop(priv->phylink); + rtl838x_hw_stop(priv); + free_irq(ndev->irq, ndev); + napi_disable(&priv->napi); + netif_stop_queue(ndev); + spin_unlock_irqrestore(&priv->lock, flags); + return 0; +} + +static void rtl838x_eth_set_multicast_list(struct net_device *dev) +{ + +} + +static void rtl838x_eth_tx_timeout(struct net_device *ndev) +{ + unsigned long flags; + struct rtl838x_eth_priv *priv = netdev_priv(ndev); + + pr_info("in %s\n", __func__); + spin_lock_irqsave(&priv->lock, flags); + rtl838x_hw_stop(priv); + rtl838x_hw_ring_setup(priv); + rtl838x_hw_en_rxtx(priv); + netif_trans_update(ndev); + netif_start_queue(ndev); + spin_unlock_irqrestore(&priv->lock, flags); +} + +static int rtl838x_eth_tx(struct sk_buff *skb, struct net_device *dev) +{ + int len, i; + struct rtl838x_eth_priv *priv = netdev_priv(dev); + struct ring_b *ring = priv->membase; + uint32_t val; + int ret; + unsigned long flags; + struct p_hdr *h; + int dest_port = -1; + + spin_lock_irqsave(&priv->lock, flags); + len = skb->len; + + /* Check for DSA tagging at the end of the buffer */ + if (netdev_uses_dsa(dev) && skb->data[len-4] == 0x80 && skb->data[len-3] > 0 + && skb->data[len-3] < 28 && skb->data[len-2] == 0x10 + && skb->data[len-1] == 0x00) { + /* Reuse tag space for CRC */ + dest_port = skb->data[len-3]; + len -= 4; + } + if (len < ETH_ZLEN) + len = ETH_ZLEN; + + /* ASIC expects that packet includes CRC, so we extend by 4 bytes */ + len += 4; + + if (skb_padto(skb, len)) { + ret = NETDEV_TX_OK; + goto txdone; + } + + /* We can send this packet if CPU owns the descriptor */ + if (!(ring->tx_r[0][ring->c_tx[0]] & 0x1)) { + /* Set descriptor for tx */ + h = &ring->tx_header[0][ring->c_tx[0]]; + + h->buf = (u8 *)CPHYSADDR(ring->tx_space); + h->size = len; + h->len = len; + + /* Create cpu_tag */ + if (dest_port > 0) { + h->cpu_tag[0] = 0x0400; + h->cpu_tag[1] = 0x0200; + h->cpu_tag[2] = 0x0000; + h->cpu_tag[3] = (1 << dest_port) >> 16; + h->cpu_tag[4] = (1 << dest_port) & 0xffff; + } else { + h->cpu_tag[0] = 0; + h->cpu_tag[1] = 0; + h->cpu_tag[2] = 0; + h->cpu_tag[3] = 0; + h->cpu_tag[4] = 0; + } + + /* Copy packet data to tx buffer */ + memcpy((void *)KSEG1ADDR(h->buf), skb->data, len); + /* Make sure packet data is visible to ASIC */ + mb(); + + /* Hand over to switch */ + ring->tx_r[0][ring->c_tx[0]] = ring->tx_r[0][ring->c_tx[0]] | 0x1; + + /* BUG: before tx fetch, need to make sure right data is accessed */ + for (i = 0; i < 10; i++) { + val = sw_r32(priv->r->dma_if_ctrl); + if ((val & 0xc) == 0xc) + break; + } + + /* Tell switch to send data */ + sw_w32_mask(0, TX_DO, priv->r->dma_if_ctrl); + + dev->stats.tx_packets++; + dev->stats.tx_bytes += len; + dev_kfree_skb(skb); + ring->c_tx[0] = (ring->c_tx[0] + 1) % TXRINGLEN; + ret = NETDEV_TX_OK; + } else { + dev_warn(&priv->pdev->dev, "Data is owned by switch\n"); + ret = NETDEV_TX_BUSY; + } +txdone: + spin_unlock_irqrestore(&priv->lock, flags); + return ret; +} + +static int rtl838x_hw_receive(struct net_device *dev, int r, int budget) +{ + struct rtl838x_eth_priv *priv = netdev_priv(dev); + struct ring_b *ring = priv->membase; + struct sk_buff *skb; + unsigned long flags; + int i, len, work_done = 0; + u8 *data, *skb_data; + unsigned int val; + u32 *last; + struct p_hdr *h; + bool dsa = netdev_uses_dsa(dev); + + spin_lock_irqsave(&priv->lock, flags); + last = (u32 *)KSEG1ADDR(sw_r32(priv->r->dma_if_rx_cur(r))); + + if (&ring->rx_r[r][ring->c_rx[r]] == last) { + spin_unlock_irqrestore(&priv->lock, flags); + return 0; + } + do { + if ((ring->rx_r[r][ring->c_rx[r]] & 0x1)) { + netdev_warn(dev, "WARNING Ring contention: ring %x, last %x, current %x, cPTR %x, ISR %x\n", r, (uint32_t)last, + (u32) &ring->rx_r[r][ring->c_rx[r]], + ring->rx_r[r][ring->c_rx[r]], + sw_r32(priv->r->dma_if_intr_sts)); + break; + } + + h = &ring->rx_header[r][ring->c_rx[r]]; + data = (u8 *)KSEG1ADDR(h->buf); + len = h->len; + + if (!len) + break; + h->buf = (u8 *)CPHYSADDR(ring->rx_space + + r * ring->c_rx[r] * RING_BUFFER); + h->size = RING_BUFFER; + h->len = 0; + work_done++; + + len -= 4; /* strip the CRC */ + /* Add 4 bytes for cpu_tag */ + if (dsa) + len += 4; + + skb = alloc_skb(len + 4, GFP_KERNEL); + skb_reserve(skb, NET_IP_ALIGN); + + if (likely(skb)) { + /* BUG: Prevent ASIC bug */ + sw_w32(0xffffffff, priv->r->dma_if_rx_ring_size(0)); + for (i = 0; i < RXRINGS; i++) { + /* Update each ring cnt */ + val = sw_r32(priv->r->dma_if_rx_ring_cntr(i)); + sw_w32(val, priv->r->dma_if_rx_ring_cntr(i)); + } + + skb_data = skb_put(skb, len); + /* Make sure data is visible */ + mb(); + memcpy(skb->data, (u8 *)KSEG1ADDR(data), len); + /* Overwrite CRC with cpu_tag */ + if (dsa) { + skb->data[len-4] = 0x80; + skb->data[len-3] = h->cpu_tag[0] & 0x1f; + skb->data[len-2] = 0x10; + skb->data[len-1] = 0x00; + } + + skb->protocol = eth_type_trans(skb, dev); + dev->stats.rx_packets++; + dev->stats.rx_bytes += len; + + netif_receive_skb(skb); + } else { + if (net_ratelimit()) + dev_warn(&dev->dev, "low on memory - packet dropped\n"); + dev->stats.rx_dropped++; + } + ring->rx_r[r][ring->c_rx[r]] + = CPHYSADDR(h) | 0x1 | (ring->c_rx[r] == (RXRINGLEN-1) ? WRAP : 0x1); + ring->c_rx[r] = (ring->c_rx[r] + 1) % RXRINGLEN; + } while (&ring->rx_r[r][ring->c_rx[r]] != last && work_done < budget); + + spin_unlock_irqrestore(&priv->lock, flags); + return work_done; +} + +static int rtl838x_poll_rx(struct napi_struct *napi, int budget) +{ + struct rtl838x_eth_priv *priv = container_of(napi, struct rtl838x_eth_priv, napi); + int work_done = 0, r = 0; + + while (work_done < budget && r < RXRINGS) { + work_done += rtl838x_hw_receive(priv->netdev, r, budget - work_done); + r++; + } + + if (work_done < budget) { + napi_complete_done(napi, work_done); + /* Enable RX interrupt */ + sw_w32(0xfffff, priv->r->dma_if_intr_msk); + } + return work_done; +} + + +static void rtl838x_validate(struct phylink_config *config, + unsigned long *supported, + struct phylink_link_state *state) +{ + __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; + + pr_info("In %s\n", __func__); + + if (!phy_interface_mode_is_rgmii(state->interface) && + state->interface != PHY_INTERFACE_MODE_1000BASEX && + state->interface != PHY_INTERFACE_MODE_MII && + state->interface != PHY_INTERFACE_MODE_REVMII && + state->interface != PHY_INTERFACE_MODE_GMII && + state->interface != PHY_INTERFACE_MODE_QSGMII && + state->interface != PHY_INTERFACE_MODE_INTERNAL && + state->interface != PHY_INTERFACE_MODE_SGMII) { + bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); + pr_err("Unsupported interface: %d\n", state->interface); + return; + } + + /* Allow all the expected bits */ + phylink_set(mask, Autoneg); + phylink_set_port_modes(mask); + phylink_set(mask, Pause); + phylink_set(mask, Asym_Pause); + + /* With the exclusion of MII and Reverse MII, we support Gigabit, + * including Half duplex + */ + if (state->interface != PHY_INTERFACE_MODE_MII && + state->interface != PHY_INTERFACE_MODE_REVMII) { + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseT_Half); + } + + phylink_set(mask, 10baseT_Half); + phylink_set(mask, 10baseT_Full); + phylink_set(mask, 100baseT_Half); + phylink_set(mask, 100baseT_Full); + + bitmap_and(supported, supported, mask, + __ETHTOOL_LINK_MODE_MASK_NBITS); + bitmap_and(state->advertising, state->advertising, mask, + __ETHTOOL_LINK_MODE_MASK_NBITS); +} + + +static void rtl838x_mac_config(struct phylink_config *config, + unsigned int mode, + const struct phylink_link_state *state) +{ + /* This is only being called for the master device, + * i.e. the CPU-Port + */ + + pr_info("In %s, mode %x\n", __func__, mode); +} + +static void rtl838x_mac_an_restart(struct phylink_config *config) +{ + struct net_device *dev = container_of(config->dev, struct net_device, dev); + struct rtl838x_eth_priv *priv = netdev_priv(dev); + + pr_info("In %s\n", __func__); + /* Restart by disabling and re-enabling link */ + sw_w32(0x6192D, priv->r->mac_force_mode_ctrl(priv->cpu_port)); + mdelay(20); + sw_w32(0x6192F, priv->r->mac_force_mode_ctrl(priv->cpu_port)); +} + +static int rtl838x_mac_pcs_get_state(struct phylink_config *config, + struct phylink_link_state *state) +{ + u32 speed; + struct net_device *dev = container_of(config->dev, struct net_device, dev); + struct rtl838x_eth_priv *priv = netdev_priv(dev); + int port = priv->cpu_port; + + pr_info("In %s\n", __func__); + + state->link = priv->r->get_mac_link_sts(port) ? 1 : 0; + state->duplex = priv->r->get_mac_link_dup_sts(port) ? 1 : 0; + + speed = priv->r->get_mac_link_spd_sts(port); + switch (speed) { + case 0: + state->speed = SPEED_10; + break; + case 1: + state->speed = SPEED_100; + break; + state->speed = SPEED_1000; + break; + default: + state->speed = SPEED_UNKNOWN; + break; + } + + state->pause &= (MLO_PAUSE_RX | MLO_PAUSE_TX); + if (priv->r->get_mac_rx_pause_sts(port)) + state->pause |= MLO_PAUSE_RX; + if (priv->r->get_mac_tx_pause_sts(port)) + state->pause |= MLO_PAUSE_TX; + + return 1; +} + +static void dump_mac_conf(struct rtl838x_eth_priv *priv) +{ + int p; + + for (p = 8; p < 16; p++) { + pr_debug("%d: %x, force %x\n", p, sw_r32(priv->r->mac_port_ctrl(p)), + sw_r32(priv->r->mac_force_mode_ctrl(p)) + ); + } + pr_debug("CPU: %x, force %x\n", sw_r32(priv->r->mac_port_ctrl(priv->cpu_port)), + sw_r32(priv->r->mac_force_mode_ctrl(priv->cpu_port)) + ); +} + +static void rtl838x_mac_link_down(struct phylink_config *config, + unsigned int mode, + phy_interface_t interface) +{ + struct net_device *dev = container_of(config->dev, struct net_device, dev); + struct rtl838x_eth_priv *priv = netdev_priv(dev); + + pr_info("In %s\n", __func__); + /* Stop TX/RX to port */ + sw_w32_mask(0x03, 0, priv->r->mac_port_ctrl(priv->cpu_port)); +} + +static void rtl838x_mac_link_up(struct phylink_config *config, unsigned int mode, + phy_interface_t interface, + struct phy_device *phy) +{ + struct net_device *dev = container_of(config->dev, struct net_device, dev); + struct rtl838x_eth_priv *priv = netdev_priv(dev); + + pr_info("In %s\n", __func__); + /* Restart TX/RX to port */ + sw_w32_mask(0, 0x03, priv->r->mac_port_ctrl(priv->cpu_port)); +} + +static void rtl838x_set_mac_hw(struct net_device *dev, u8 *mac) +{ + struct rtl838x_eth_priv *priv = netdev_priv(dev); + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + pr_info("In %s\n", __func__); + sw_w32((mac[0] << 8) | mac[1], priv->r->mac); + sw_w32((mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5], priv->r->mac + 4); + + if (priv->family_id == RTL8380_FAMILY_ID) { + /* 2 more registers, ALE/MAC block */ + sw_w32((mac[0] << 8) | mac[1], RTL838X_MAC_ALE); + sw_w32((mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5], + (RTL838X_MAC_ALE + 4)); + + sw_w32((mac[0] << 8) | mac[1], RTL838X_MAC2); + sw_w32((mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5], + RTL838X_MAC2 + 4); + } + spin_unlock_irqrestore(&priv->lock, flags); +} + +static int rtl838x_set_mac_address(struct net_device *dev, void *p) +{ + struct rtl838x_eth_priv *priv = netdev_priv(dev); + const struct sockaddr *addr = p; + u8 *mac = (u8 *) (addr->sa_data); + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); + rtl838x_set_mac_hw(dev, mac); + + pr_info("Using MAC %08x%08x\n", sw_r32(priv->r->mac), sw_r32(priv->r->mac + 4)); + return 0; +} + +static int rtl8390_init_mac(struct rtl838x_eth_priv *priv) +{ + pr_info("Configuring RTL8390 MAC\n"); + // from mac_config_init + sw_w32(0x80, RTL839X_MAC_EFUSE_CTRL); + sw_w32(0x4, RTL839X_RST_GLB_CTRL); + sw_w32(0x3c324f40, RTL839X_MAC_GLB_CTRL); + /* Unlimited egress rate */ + sw_w32(0x1297b961, RTL839X_SCHED_LB_TICK_TKN_CTRL); + + return 0; +} + +static int rtl8380_init_mac(struct rtl838x_eth_priv *priv) +{ + int i; + + if (priv->family_id == 0x8390) + return rtl8390_init_mac(priv); + + if (priv->family_id != 0x8380) + return 0; + + pr_info("%s\n", __func__); + /* fix timer for EEE */ + sw_w32(0x5001411, RTL838X_EEE_TX_TIMER_GIGA_CTRL); + sw_w32(0x5001417, RTL838X_EEE_TX_TIMER_GELITE_CTRL); + + /* Init VLAN */ + if (priv->id == 0x8382) { + for (i = 0; i <= 28; i++) + sw_w32(0, 0xd57c + i * 0x80); + } + if (priv->id == 0x8380) { + for (i = 8; i <= 28; i++) + sw_w32(0, 0xd57c + i * 0x80); + } + return 0; +} + +static int rtl838x_get_link_ksettings(struct net_device *ndev, + struct ethtool_link_ksettings *cmd) +{ + struct rtl838x_eth_priv *priv = netdev_priv(ndev); + + pr_info("%s called\n", __func__); + return phylink_ethtool_ksettings_get(priv->phylink, cmd); +} + +static int rtl838x_set_link_ksettings(struct net_device *ndev, + const struct ethtool_link_ksettings *cmd) +{ + struct rtl838x_eth_priv *priv = netdev_priv(ndev); + + pr_info("%s called\n", __func__); + return phylink_ethtool_ksettings_set(priv->phylink, cmd); +} + + +static int rtl838x_mdio_read(struct mii_bus *bus, int mii_id, int regnum) +{ + u32 val; + u32 offset = 0; + int err; + struct rtl838x_eth_priv *priv = bus->priv; + + if (mii_id >= 24 && mii_id <= 27 && priv->id == 0x8380) { + if (mii_id == 26) + offset = 0x100; + val = sw_r32(MAPLE_SDS4_FIB_REG0r + offset + (regnum << 2)) & 0xffff; + return val; + } + err = rtl838x_read_phy(mii_id, 0, regnum, &val); + if (err) + return err; + return val; +} + +static int rtl839x_mdio_read(struct mii_bus *bus, int mii_id, int regnum) +{ + u32 val; + int err; + + err = rtl839x_read_phy(mii_id, 0, regnum, &val); + if (err) + return err; + return val; +} + +static int rtl838x_mdio_write(struct mii_bus *bus, int mii_id, + int regnum, u16 value) +{ + u32 offset = 0; + struct rtl838x_eth_priv *priv = bus->priv; + + if (mii_id >= 24 && mii_id <= 27 && priv->id == 0x8380) { + if (mii_id == 26) + offset = 0x100; + sw_w32(value, MAPLE_SDS4_FIB_REG0r + offset + (regnum << 2)); + return 0; + } + return rtl838x_write_phy(mii_id, 0, regnum, value); +} + +static int rtl839x_mdio_write(struct mii_bus *bus, int mii_id, + int regnum, u16 value) +{ + return rtl839x_write_phy(mii_id, 0, regnum, value); +} + +static int rtl838x_mdio_reset(struct mii_bus *bus) +{ + pr_info("%s called\n", __func__); + /* Disable MAC polling the PHY so that we can start configuration */ + sw_w32(0x00000000, RTL838X_SMI_POLL_CTRL); + + /* Enable PHY control via SoC */ + sw_w32_mask(0, 1 << 15, RTL838X_SMI_GLB_CTRL); + + return 0; +} + +static int rtl839x_mdio_reset(struct mii_bus *bus) +{ + pr_info("%s called\n", __func__); + /* Disable MAC polling the PHY so that we can start configuration */ + sw_w32(0x00000000, RTL839X_SMI_PORT_POLLING_CTRL); + sw_w32(0x00000000, RTL839X_SMI_PORT_POLLING_CTRL + 4); + /* Disable PHY polling via SoC */ + sw_w32_mask(1 << 7, 0, RTL839X_SMI_GLB_CTRL); + + return 0; +} + + +static int rtl838x_mdio_init(struct rtl838x_eth_priv *priv) +{ + struct device_node *mii_np; + int ret; + + pr_info("%s called\n", __func__); + mii_np = of_get_child_by_name(priv->pdev->dev.of_node, "mdio-bus"); + + if (!mii_np) { + dev_err(&priv->pdev->dev, "no %s child node found", "mdio-bus"); + return -ENODEV; + } + + if (!of_device_is_available(mii_np)) { + ret = -ENODEV; + goto err_put_node; + } + + priv->mii_bus = devm_mdiobus_alloc(&priv->pdev->dev); + if (!priv->mii_bus) { + ret = -ENOMEM; + goto err_put_node; + } + + if (priv->family_id == RTL8380_FAMILY_ID) { + priv->mii_bus->name = "rtl838x-eth-mdio"; + priv->mii_bus->read = rtl838x_mdio_read; + priv->mii_bus->write = rtl838x_mdio_write; + priv->mii_bus->reset = rtl838x_mdio_reset; + } else { + priv->mii_bus->name = "rtl839x-eth-mdio"; + priv->mii_bus->read = rtl839x_mdio_read; + priv->mii_bus->write = rtl839x_mdio_write; + priv->mii_bus->reset = rtl839x_mdio_reset; + } + priv->mii_bus->priv = priv; + priv->mii_bus->parent = &priv->pdev->dev; + + snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "%pOFn", mii_np); + ret = of_mdiobus_register(priv->mii_bus, mii_np); + +err_put_node: + of_node_put(mii_np); + return ret; +} + +static int rtl838x_mdio_remove(struct rtl838x_eth_priv *priv) +{ + pr_info("%s called\n", __func__); + if (!priv->mii_bus) + return 0; + + mdiobus_unregister(priv->mii_bus); + mdiobus_free(priv->mii_bus); + + return 0; +} + +static const struct net_device_ops rtl838x_eth_netdev_ops = { + .ndo_open = rtl838x_eth_open, + .ndo_stop = rtl838x_eth_stop, + .ndo_start_xmit = rtl838x_eth_tx, + .ndo_set_mac_address = rtl838x_set_mac_address, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_rx_mode = rtl838x_eth_set_multicast_list, + .ndo_tx_timeout = rtl838x_eth_tx_timeout, +}; + +static const struct phylink_mac_ops rtl838x_phylink_ops = { + .validate = rtl838x_validate, + .mac_link_state = rtl838x_mac_pcs_get_state, + .mac_an_restart = rtl838x_mac_an_restart, + .mac_config = rtl838x_mac_config, + .mac_link_down = rtl838x_mac_link_down, + .mac_link_up = rtl838x_mac_link_up, +}; + +static const struct ethtool_ops rtl838x_ethtool_ops = { + .get_link_ksettings = rtl838x_get_link_ksettings, + .set_link_ksettings = rtl838x_set_link_ksettings, +}; + +static int __init rtl838x_eth_probe(struct platform_device *pdev) +{ + struct net_device *dev; + struct device_node *dn = pdev->dev.of_node; + struct rtl838x_eth_priv *priv; + struct resource *res, *mem; + const void *mac; + phy_interface_t phy_mode; + struct phylink *phylink; + int err = 0; + + pr_info("Probing RTL838X eth device pdev: %x, dev: %x\n", + (u32)pdev, (u32)(&(pdev->dev))); + + if (!dn) { + dev_err(&pdev->dev, "No DT found\n"); + return -EINVAL; + } + + dev = alloc_etherdev(sizeof(struct rtl838x_eth_priv)); + if (!dev) { + err = -ENOMEM; + goto err_free; + } + SET_NETDEV_DEV(dev, &pdev->dev); + priv = netdev_priv(dev); + + /* obtain buffer memory space */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res) { + mem = devm_request_mem_region(&pdev->dev, res->start, + resource_size(res), res->name); + if (!mem) { + dev_err(&pdev->dev, "cannot request memory space\n"); + err = -ENXIO; + goto err_free; + } + + dev->mem_start = mem->start; + dev->mem_end = mem->end; + } else { + dev_err(&pdev->dev, "cannot request IO resource\n"); + err = -ENXIO; + goto err_free; + } + + /* Allocate buffer memory */ + priv->membase = dmam_alloc_coherent(&pdev->dev, + sizeof(struct ring_b), (void *)&dev->mem_start, + GFP_KERNEL); + if (!priv->membase) { + dev_err(&pdev->dev, "cannot allocate DMA buffer\n"); + err = -ENOMEM; + goto err_free; + } + + spin_lock_init(&priv->lock); + + /* obtain device IRQ number */ + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(&pdev->dev, "cannot obtain IRQ, using default 24\n"); + dev->irq = 24; + } else { + dev->irq = res->start; + } + dev->ethtool_ops = &rtl838x_ethtool_ops; + + priv->id = soc_info.id; + priv->family_id = soc_info.family; + if (priv->id) { + pr_info("Found SoC ID: %4x: %s, family %x\n", + priv->id, soc_info.name, priv->family_id); + } else { + pr_err("Unknown chip id (%04x)\n", priv->id); + return -ENODEV; + } + + if (priv->family_id == 0x8390) { + priv->cpu_port = RTL839X_CPU_PORT; + priv->r = &rtl839x_reg; + } else { + priv->cpu_port = RTL838X_CPU_PORT; + priv->r = &rtl838x_reg; + } + + rtl8380_init_mac(priv); + + /* try to get mac address in the following order: + * 1) from device tree data + * 2) from internal registers set by bootloader + */ + mac = of_get_mac_address(pdev->dev.of_node); + if (!IS_ERR(mac)) { + memcpy(dev->dev_addr, mac, ETH_ALEN); + rtl838x_set_mac_hw(dev, (u8 *)mac); + } else { + dev->dev_addr[0] = (sw_r32(priv->r->mac) >> 8) & 0xff; + dev->dev_addr[1] = sw_r32(priv->r->mac) & 0xff; + dev->dev_addr[2] = (sw_r32(priv->r->mac + 4) >> 24) & 0xff; + dev->dev_addr[3] = (sw_r32(priv->r->mac + 4) >> 16) & 0xff; + dev->dev_addr[4] = (sw_r32(priv->r->mac + 4) >> 8) & 0xff; + dev->dev_addr[5] = sw_r32(priv->r->mac + 4) & 0xff; + } + /* if the address is invalid, use a random value */ + if (!is_valid_ether_addr(dev->dev_addr)) { + struct sockaddr sa = { AF_UNSPEC }; + + netdev_warn(dev, "Invalid MAC address, using random\n"); + eth_hw_addr_random(dev); + memcpy(sa.sa_data, dev->dev_addr, ETH_ALEN); + if (rtl838x_set_mac_address(dev, &sa)) + netdev_warn(dev, "Failed to set MAC address.\n"); + } + pr_info("Using MAC %08x%08x\n", sw_r32(priv->r->mac), + sw_r32(priv->r->mac + 4)); + strcpy(dev->name, "eth%d"); + dev->netdev_ops = &rtl838x_eth_netdev_ops; + priv->pdev = pdev; + priv->netdev = dev; + + err = rtl838x_mdio_init(priv); + if (err) + goto err_free; + + err = register_netdev(dev); + if (err) + goto err_free; + + netif_napi_add(dev, &priv->napi, rtl838x_poll_rx, 64); + platform_set_drvdata(pdev, dev); + + phy_mode = of_get_phy_mode(dn); + if (phy_mode < 0) { + dev_err(&pdev->dev, "incorrect phy-mode\n"); + err = -EINVAL; + goto err_free; + } + priv->phylink_config.dev = &dev->dev; + priv->phylink_config.type = PHYLINK_NETDEV; + + phylink = phylink_create(&priv->phylink_config, pdev->dev.fwnode, + phy_mode, &rtl838x_phylink_ops); + if (IS_ERR(phylink)) { + err = PTR_ERR(phylink); + goto err_free; + } + priv->phylink = phylink; + return 0; + +err_free: + pr_err("Error setting up netdev, freeing it again.\n"); + free_netdev(dev); + return err; +} + +static int rtl838x_eth_remove(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct rtl838x_eth_priv *priv = netdev_priv(dev); + + if (dev) { + pr_info("Removing platform driver for rtl838x-eth\n"); + rtl838x_mdio_remove(priv); + rtl838x_hw_stop(priv); + netif_stop_queue(dev); + netif_napi_del(&priv->napi); + unregister_netdev(dev); + free_netdev(dev); + } + return 0; +} + +static const struct of_device_id rtl838x_eth_of_ids[] = { + { .compatible = "realtek,rtl838x-eth"}, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rtl838x_eth_of_ids); + +static struct platform_driver rtl838x_eth_driver = { + .probe = rtl838x_eth_probe, + .remove = rtl838x_eth_remove, + .driver = { + .name = "rtl838x-eth", + .pm = NULL, + .of_match_table = rtl838x_eth_of_ids, + }, +}; + +module_platform_driver(rtl838x_eth_driver); + +MODULE_AUTHOR("B. Koblitz"); +MODULE_DESCRIPTION("RTL838X SoC Ethernet Driver"); +MODULE_LICENSE("GPL"); diff --git a/target/linux/rtl838x/files-5.4/drivers/net/ethernet/rtl838x_eth.h b/target/linux/rtl838x/files-5.4/drivers/net/ethernet/rtl838x_eth.h new file mode 100644 index 0000000000..53ab7e5eb2 --- /dev/null +++ b/target/linux/rtl838x/files-5.4/drivers/net/ethernet/rtl838x_eth.h @@ -0,0 +1,261 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _RTL838X_ETH_H +#define _RTL838X_ETH_H + +/* + * Register definition + */ + +#define RTL838X_CPU_PORT 28 +#define RTL839X_CPU_PORT 52 + +#define RTL838X_MAC_PORT_CTRL (0xd560) +#define RTL839X_MAC_PORT_CTRL (0x8004) +#define RTL838X_DMA_IF_INTR_STS (0x9f54) +#define RTL839X_DMA_IF_INTR_STS (0x7868) +#define RTL838X_DMA_IF_INTR_MSK (0x9f50) +#define RTL839X_DMA_IF_INTR_MSK (0x7864) +#define RTL838X_DMA_IF_CTRL (0x9f58) +#define RTL839X_DMA_IF_CTRL (0x786c) +#define RTL838X_RST_GLB_CTRL_0 (0x003c) +#define RTL838X_MAC_FORCE_MODE_CTRL (0xa104) +#define RTL839X_MAC_FORCE_MODE_CTRL (0x02bc) + +/* MAC address settings */ +#define RTL838X_MAC (0xa9ec) +#define RTL839X_MAC (0x02b4) +#define RTL838X_MAC_ALE (0x6b04) +#define RTL838X_MAC2 (0xa320) + +#define RTL838X_DMA_RX_BASE (0x9f00) +#define RTL839X_DMA_RX_BASE (0x780c) +#define RTL838X_DMA_TX_BASE (0x9f40) +#define RTL839X_DMA_TX_BASE (0x784c) +#define RTL838X_DMA_IF_RX_RING_SIZE (0xB7E4) +#define RTL839X_DMA_IF_RX_RING_SIZE (0x6038) +#define RTL838X_DMA_IF_RX_RING_CNTR (0xB7E8) +#define RTL839X_DMA_IF_RX_RING_CNTR (0x603c) +#define RTL838X_DMA_IF_RX_CUR (0x9F20) +#define RTL839X_DMA_IF_RX_CUR (0x782c) + +#define RTL838X_DMY_REG31 (0x3b28) +#define RTL838X_SDS_MODE_SEL (0x0028) +#define RTL838X_SDS_CFG_REG (0x0034) +#define RTL838X_INT_MODE_CTRL (0x005c) +#define RTL838X_CHIP_INFO (0x00d8) +#define RTL838X_SDS4_REG28 (0xef80) +#define RTL838X_SDS4_DUMMY0 (0xef8c) +#define RTL838X_SDS5_EXT_REG6 (0xf18c) +#define RTL838X_PORT_ISO_CTRL(port) (0x4100 + ((port) << 2)) +#define RTL838X_STAT_PORT_STD_MIB(port) (0x1200 + (((port) << 8))) +#define RTL838X_STAT_RST (0x3100) +#define RTL838X_STAT_CTRL (0x3108) + +/* Registers of the internal Serdes of the 8380 */ +#define MAPLE_SDS4_REG0r RTL838X_SDS4_REG28 +#define MAPLE_SDS5_REG0r (RTL838X_SDS4_REG28 + 0x100) +#define MAPLE_SDS4_REG3r RTL838X_SDS4_DUMMY0 +#define MAPLE_SDS5_REG3r (RTL838X_SDS4_REG28 + 0x100) +#define MAPLE_SDS4_FIB_REG0r (RTL838X_SDS4_REG28 + 0x880) +#define MAPLE_SDS5_FIB_REG0r (RTL838X_SDS4_REG28 + 0x980) + +/* VLAN registers */ +#define RTL838X_VLAN_PROFILE(idx) (0x3A88 + ((idx) << 2)) +#define RTL838X_VLAN_PORT_EGR_FLTR (0x3A84) +#define RTL838X_VLAN_PORT_PB_VLAN(port) (0x3C00 + ((port) << 2)) +#define RTL838X_VLAN_PORT_IGR_FLTR_0 (0x3A7C) +#define RTL838X_VLAN_PORT_IGR_FLTR_1 (0x3A7C + 4) +#define RTL838X_TBL_ACCESS_CTRL_0 (0x6914) +#define RTL838X_TBL_ACCESS_DATA_0(idx) (0x6918 + ((idx) << 2)) +#define RTL838X_TBL_ACCESS_CTRL_1 (0xA4C8) +#define RTL838X_TBL_ACCESS_DATA_1(idx) (0xA4CC + ((idx) << 2)) +#define RTL839X_TBL_ACCESS_L2_CTRL (0x1180) +#define RTL839X_TBL_ACCESS_L2_DATA(idx) (0x1184 + ((idx) << 2)) +/* MAC handling */ +#define RTL838X_MAC_LINK_STS (0xa188) +#define RTL839X_MAC_LINK_STS (0x0390) +#define RTL838X_MAC_LINK_SPD_STS (0xa190) +#define RTL839X_MAC_LINK_SPD_STS (0x03a0) +#define RTL838X_MAC_LINK_DUP_STS (0xa19c) +#define RTL839X_MAC_LINK_DUP_STS (0x03b0) +// TODO: RTL8390_MAC_LINK_MEDIA_STS_ADDR ??? +#define RTL838X_MAC_TX_PAUSE_STS (0xa1a0) +#define RTL839X_MAC_TX_PAUSE_STS (0x03b8) +#define RTL838X_MAC_RX_PAUSE_STS (0xa1a4) +#define RTL839X_MAC_RX_PAUSE_STS (0x03c0) +#define RTL838X_EEE_TX_TIMER_GIGA_CTRL (0xaa04) +#define RTL838X_EEE_TX_TIMER_GELITE_CTRL (0xaa08) +#define RTL839X_MAC_GLB_CTRL (0x02a8) +#define RTL839X_SCHED_LB_TICK_TKN_CTRL (0x60f8) + +#define RTL838X_L2_TBL_FLUSH_CTRL (0x3370) +#define RTL839X_L2_TBL_FLUSH_CTRL (0x3ba0) + +/* MAC link state bits */ +#define FORCE_EN (1 << 0) +#define FORCE_LINK_EN (1 << 1) +#define NWAY_EN (1 << 2) +#define DUPLX_MODE (1 << 3) +#define TX_PAUSE_EN (1 << 6) +#define RX_PAUSE_EN (1 << 7) + +inline int rtl838x_mac_port_ctrl(int p) +{ + return RTL838X_MAC_PORT_CTRL + (p << 7); +} + +inline int rtl839x_mac_port_ctrl(int p) +{ + return RTL839X_MAC_PORT_CTRL + (p << 7); +} + +static inline int rtl838x_mac_force_mode_ctrl(int p) +{ + return RTL838X_MAC_FORCE_MODE_CTRL + (p << 2); +} + +static inline int rtl839x_mac_force_mode_ctrl(int p) +{ + return RTL839X_MAC_FORCE_MODE_CTRL + (p << 2); +} + +inline int rtl838x_dma_rx_base(int i) +{ + return RTL838X_DMA_RX_BASE + (i << 2); +} + +inline int rtl839x_dma_rx_base(int i) +{ + return RTL839X_DMA_RX_BASE + (i << 2); +} + +inline int rtl838x_dma_tx_base(int i) +{ + return RTL838X_DMA_TX_BASE + (i << 2); +} + +inline int rtl839x_dma_tx_base(int i) +{ + return RTL839X_DMA_TX_BASE + (i << 2); +} + +inline int rtl838x_dma_if_rx_ring_size(int i) +{ + return RTL838X_DMA_IF_RX_RING_SIZE + ((i >> 3) << 2); +} + +inline int rtl839x_dma_if_rx_ring_size(int i) +{ + return RTL839X_DMA_IF_RX_RING_SIZE + ((i >> 3) << 2); +} + +inline int rtl838x_dma_if_rx_ring_cntr(int i) +{ + return RTL838X_DMA_IF_RX_RING_CNTR + ((i >> 3) << 2); +} + +inline int rtl839x_dma_if_rx_ring_cntr(int i) +{ + return RTL839X_DMA_IF_RX_RING_CNTR + ((i >> 3) << 2); +} + + +inline int rtl838x_dma_if_rx_cur(int i) +{ + return RTL838X_DMA_IF_RX_CUR + (i << 2); +} + +inline int rtl839x_dma_if_rx_cur(int i) +{ + return RTL839X_DMA_IF_RX_CUR + (i << 2); +} + +inline u32 rtl838x_get_mac_link_sts(int port) +{ + return (sw_r32(RTL838X_MAC_LINK_STS) & (1 << port)); +} + +inline u32 rtl839x_get_mac_link_sts(int p) +{ + return (sw_r32(RTL839X_MAC_LINK_STS + ((p >> 5) << 2)) & (1 << p)); +} + +inline u32 rtl838x_get_mac_link_dup_sts(int port) +{ + return (sw_r32(RTL838X_MAC_LINK_DUP_STS) & (1 << port)); +} + +inline u32 rtl839x_get_mac_link_dup_sts(int p) +{ + return (sw_r32(RTL839X_MAC_LINK_DUP_STS + ((p >> 5) << 2)) & (1 << p)); +} + +inline u32 rtl838x_get_mac_link_spd_sts(int port) +{ + int r = RTL838X_MAC_LINK_SPD_STS + ((port >> 4) << 2); + u32 speed = sw_r32(r); + + speed >>= (port % 16) << 1; + return (speed & 0x3); +} + +inline u32 rtl839x_get_mac_link_spd_sts(int port) +{ + int r = RTL839X_MAC_LINK_SPD_STS + ((port >> 4) << 2); + u32 speed = sw_r32(r); + + speed >>= (port % 16) << 1; + return (speed & 0x3); +} + +inline u32 rtl838x_get_mac_rx_pause_sts(int port) +{ + return (sw_r32(RTL838X_MAC_RX_PAUSE_STS) & (1 << port)); +} + +inline u32 rtl839x_get_mac_rx_pause_sts(int p) +{ + return (sw_r32(RTL839X_MAC_RX_PAUSE_STS + ((p >> 5) << 2)) & (1 << p)); +} + +inline u32 rtl838x_get_mac_tx_pause_sts(int port) +{ + return (sw_r32(RTL838X_MAC_TX_PAUSE_STS) & (1 << port)); +} + +inline u32 rtl839x_get_mac_tx_pause_sts(int p) +{ + return (sw_r32(RTL839X_MAC_TX_PAUSE_STS + ((p >> 5) << 2)) & (1 << p)); +} + + +struct rtl838x_reg { + int (*mac_port_ctrl)(int port); + int dma_if_intr_sts; + int dma_if_intr_msk; + int dma_if_ctrl; + int (*mac_force_mode_ctrl)(int port); + int (*dma_rx_base)(int ring); + int (*dma_tx_base)(int ring); + int (*dma_if_rx_ring_size)(int ring); + int (*dma_if_rx_ring_cntr)(int ring); + int (*dma_if_rx_cur)(int ring); + int rst_glb_ctrl; + u32 (*get_mac_link_sts)(int port); + u32 (*get_mac_link_dup_sts)(int port); + u32 (*get_mac_link_spd_sts)(int port); + u32 (*get_mac_rx_pause_sts)(int port); + u32 (*get_mac_tx_pause_sts)(int port); + int mac; + int l2_tbl_flush_ctrl; +}; + +int rtl838x_write_phy(u32 port, u32 page, u32 reg, u32 val); +int rtl838x_read_phy(u32 port, u32 page, u32 reg, u32 *val); +int rtl839x_write_phy(u32 port, u32 page, u32 reg, u32 val); +int rtl839x_read_phy(u32 port, u32 page, u32 reg, u32 *val); + +extern int rtl8380_sds_power(int mac, int val); + +#endif /* _RTL838X_ETH_H */ diff --git a/target/linux/rtl838x/image/Makefile b/target/linux/rtl838x/image/Makefile new file mode 100644 index 0000000000..eef1fe0a33 --- /dev/null +++ b/target/linux/rtl838x/image/Makefile @@ -0,0 +1,44 @@ +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# +include $(TOPDIR)/rules.mk +include $(INCLUDE_DIR)/image.mk + +KERNEL_LOADADDR = 0x80000000 +KERNEL_ENTRY = 0x80000400 + +define Build/custom-uimage + mkimage -A $(LINUX_KARCH) \ + -O linux -T kernel \ + -C gzip -a $(KERNEL_LOADADDR) $(if $(UIMAGE_MAGIC),-M $(UIMAGE_MAGIC),) \ + -e $(if $(KERNEL_ENTRY),$(KERNEL_ENTRY),$(KERNEL_LOADADDR)) \ + -n '$(1)' -d $@ $@.new + mv $@.new $@ +endef + + +define Device/Default + PROFILES = Default + KERNEL := kernel-bin | append-dtb | gzip | uImage gzip + KERNEL_INITRAMFS := kernel-bin | append-dtb | gzip | uImage gzip + DEVICE_DTS_DIR := ../dts + DEVICE_DTS = $$(SOC)_$(1) + SUPPORTED_DEVICES := $(subst _,$(comma),$(1)) + IMAGES := sysupgrade.bin + IMAGE/sysupgrade.bin := append-kernel | pad-to 64k | append-rootfs | pad-rootfs | \ + append-metadata | check-size +endef + +define Device/allnet_all-sg8208m + SOC := rtl8382 + IMAGE_SIZE := 7168k + DEVICE_VENDOR := ALLNET + DEVICE_MODEL := ALL-SG8208M + UIMAGE_MAGIC := 0x00000006 + KERNEL := kernel-bin | append-dtb | gzip | custom-uimage 2.2.2.0 + KERNEL_INITRAMFS := kernel-bin | append-dtb | gzip | custom-uimage 2.2.2.0 + DEVICE_PACKAGES := ip-full ip-bridge kmod-gpio-button-hotplug tc +endef +TARGET_DEVICES += allnet_all-sg8208m + +$(eval $(call BuildImage)) diff --git a/target/linux/rtl838x/patches-5.4/300-mips-add-rtl838x-platform.patch b/target/linux/rtl838x/patches-5.4/300-mips-add-rtl838x-platform.patch new file mode 100644 index 0000000000..34fb959a50 --- /dev/null +++ b/target/linux/rtl838x/patches-5.4/300-mips-add-rtl838x-platform.patch @@ -0,0 +1,42 @@ +--- a/arch/mips/Kbuild.platforms ++++ b/arch/mips/Kbuild.platforms +@@ -27,6 +27,7 @@ platforms += pistachio + platforms += pmcs-msp71xx + platforms += pnx833x + platforms += ralink ++platforms += rtl838x + platforms += rb532 + platforms += sgi-ip22 + platforms += sgi-ip27 +--- a/arch/mips/Kconfig ++++ b/arch/mips/Kconfig +@@ -629,6 +629,29 @@ config RALINK + select ARCH_HAS_RESET_CONTROLLER + select RESET_CONTROLLER + ++config RTL838X ++ bool "Realtek based platforms" ++ select DMA_NONCOHERENT ++ select IRQ_MIPS_CPU ++ select CSRC_R4K ++ select CEVT_R4K ++ select SYS_HAS_CPU_MIPS32_R1 ++ select SYS_HAS_CPU_MIPS32_R2 ++ select SYS_SUPPORTS_BIG_ENDIAN ++ select SYS_SUPPORTS_32BIT_KERNEL ++ select SYS_SUPPORTS_MIPS16 ++ select SYS_SUPPORTS_MULTITHREADING ++ select SYS_SUPPORTS_VPE_LOADER ++ select SYS_HAS_EARLY_PRINTK ++ select SWAP_IO_SPACE ++ select BOOT_RAW ++ select CLKDEV_LOOKUP ++ select PINCTRL ++ select ARCH_HAS_RESET_CONTROLLER ++ select RESET_CONTROLLER ++ select RTL8380_SERIES ++ select USE_OF ++ + config SGI_IP22 + bool "SGI IP22 (Indy/Indigo2)" + select FW_ARC diff --git a/target/linux/rtl838x/patches-5.4/301-gpio-add-rtl838x-driver.patch b/target/linux/rtl838x/patches-5.4/301-gpio-add-rtl838x-driver.patch new file mode 100644 index 0000000000..5eb8521ccb --- /dev/null +++ b/target/linux/rtl838x/patches-5.4/301-gpio-add-rtl838x-driver.patch @@ -0,0 +1,25 @@ +--- a/drivers/gpio/Kconfig ++++ b/drivers/gpio/Kconfig +@@ -441,6 +441,12 @@ + A 32-bit single register GPIO fixed in/out implementation. This + can be used to represent any register as a set of GPIO signals. + ++config GPIO_RTL838X ++ tristate "RTL838X GPIO" ++ depends on RTL838X ++ help ++ Say yes here to support RTL838X GPIO devices. ++ + config GPIO_SAMA5D2_PIOBU + tristate "SAMA5D2 PIOBU GPIO support" + depends on MFD_SYSCON +--- a/drivers/gpio/Makefile ++++ b/drivers/gpio/Makefile +@@ -117,6 +117,7 @@ + obj-$(CONFIG_GPIO_RCAR) += gpio-rcar.o + obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o + obj-$(CONFIG_GPIO_REG) += gpio-reg.o ++obj-$(CONFIG_GPIO_RTL838X) += gpio-rtl838x.o + obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o + obj-$(CONFIG_GPIO_SAMA5D2_PIOBU) += gpio-sama5d2-piobu.o + obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o diff --git a/target/linux/rtl838x/patches-5.4/400-mtd-add-rtl838x-spi-flash-driver.patch b/target/linux/rtl838x/patches-5.4/400-mtd-add-rtl838x-spi-flash-driver.patch new file mode 100644 index 0000000000..16cff75308 --- /dev/null +++ b/target/linux/rtl838x/patches-5.4/400-mtd-add-rtl838x-spi-flash-driver.patch @@ -0,0 +1,23 @@ +--- a/drivers/mtd/spi-nor/Kconfig ++++ b/drivers/mtd/spi-nor/Kconfig +@@ -118,4 +118,13 @@ config SPI_INTEL_SPI_PLATFORM + To compile this driver as a module, choose M here: the module + will be called intel-spi-platform. + ++config SPI_RTL838X ++ tristate "Realtek RTl838X SPI flash platform driver" ++ depends on RTL838X ++ help ++ This driver provides support for accessing SPI flash ++ in the RTL838X SoC. ++ ++ Say N here unless you know what you are doing. ++ + endif # MTD_SPI_NOR +--- a/drivers/mtd/spi-nor/Makefile ++++ b/drivers/mtd/spi-nor/Makefile +@@ -8,3 +8,4 @@ obj-$(CONFIG_SPI_NXP_SPIFI) += nxp-spifi + obj-$(CONFIG_SPI_INTEL_SPI) += intel-spi.o + obj-$(CONFIG_SPI_INTEL_SPI_PCI) += intel-spi-pci.o + obj-$(CONFIG_SPI_INTEL_SPI_PLATFORM) += intel-spi-platform.o ++obj-$(CONFIG_SPI_RTL838X) += rtl838x-nor.o diff --git a/target/linux/rtl838x/patches-5.4/700-net-dsa-add-support-for-rtl838x-switch.patch b/target/linux/rtl838x/patches-5.4/700-net-dsa-add-support-for-rtl838x-switch.patch new file mode 100644 index 0000000000..0082176d83 --- /dev/null +++ b/target/linux/rtl838x/patches-5.4/700-net-dsa-add-support-for-rtl838x-switch.patch @@ -0,0 +1,26 @@ +--- a/drivers/net/dsa/Kconfig ++++ b/drivers/net/dsa/Kconfig +@@ -74,6 +74,13 @@ config NET_DSA_REALTEK_SMI + This enables support for the Realtek SMI-based switch + chips, currently only RTL8366RB. + ++config NET_DSA_RTL838X ++ tristate "Realtek rtl838x switch support" ++ depends on RTL838X ++ select NET_DSA_TAG_TRAILER ++ ---help--- ++ Say Y here if you want to use the Realtek rtl838x switch. ++ + config NET_DSA_SMSC_LAN9303 + tristate + select NET_DSA_TAG_LAN9303 +--- a/drivers/net/dsa/Makefile ++++ b/drivers/net/dsa/Makefile +@@ -11,6 +11,7 @@ obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e + obj-$(CONFIG_NET_DSA_QCA8K) += qca8k.o + obj-$(CONFIG_NET_DSA_REALTEK_SMI) += realtek-smi.o + realtek-smi-objs := realtek-smi-core.o rtl8366.o rtl8366rb.o ++obj-$(CONFIG_NET_DSA_RTL838X) += rtl838x_sw.o rtl838x_phy.o + obj-$(CONFIG_NET_DSA_SMSC_LAN9303) += lan9303-core.o + obj-$(CONFIG_NET_DSA_SMSC_LAN9303_I2C) += lan9303_i2c.o + obj-$(CONFIG_NET_DSA_SMSC_LAN9303_MDIO) += lan9303_mdio.o diff --git a/target/linux/rtl838x/patches-5.4/701-net-dsa-add-rtl838x-support-for-tag-trailer.patch b/target/linux/rtl838x/patches-5.4/701-net-dsa-add-rtl838x-support-for-tag-trailer.patch new file mode 100644 index 0000000000..d45c547803 --- /dev/null +++ b/target/linux/rtl838x/patches-5.4/701-net-dsa-add-rtl838x-support-for-tag-trailer.patch @@ -0,0 +1,37 @@ +--- a/net/dsa/tag_trailer.c ++++ b/net/dsa/tag_trailer.c +@@ -44,7 +44,12 @@ static struct sk_buff *trailer_xmit(stru + + trailer = skb_put(nskb, 4); + trailer[0] = 0x80; ++ ++#ifdef CONFIG_NET_DSA_RTL838X ++ trailer[1] = dp->index; ++#else + trailer[1] = 1 << dp->index; ++#endif /* CONFIG_NET_DSA_RTL838X */ + trailer[2] = 0x10; + trailer[3] = 0x00; + +@@ -61,12 +66,20 @@ static struct sk_buff *trailer_rcv(struc + return NULL; + + trailer = skb_tail_pointer(skb) - 4; ++ ++#ifdef CONFIG_NET_DSA_RTL838X ++ if (trailer[0] != 0x80 || (trailer[1] & 0xe0) != 0x00 || ++ (trailer[2] & 0xef) != 0x00 || trailer[3] != 0x00) ++ return NULL; ++ ++ source_port = trailer[1] & 0x1f; ++#else + if (trailer[0] != 0x80 || (trailer[1] & 0xf8) != 0x00 || + (trailer[2] & 0xef) != 0x00 || trailer[3] != 0x00) + return NULL; + + source_port = trailer[1] & 7; +- ++#endif + skb->dev = dsa_master_find_slave(dev, 0, source_port); + if (!skb->dev) + return NULL; diff --git a/target/linux/rtl838x/patches-5.4/702-net-dsa-increase-dsa-max-ports-for-rtl838x.patch b/target/linux/rtl838x/patches-5.4/702-net-dsa-increase-dsa-max-ports-for-rtl838x.patch new file mode 100644 index 0000000000..5329dcad59 --- /dev/null +++ b/target/linux/rtl838x/patches-5.4/702-net-dsa-increase-dsa-max-ports-for-rtl838x.patch @@ -0,0 +1,13 @@ +Index: linux-5.4.24/include/linux/platform_data/dsa.h +=================================================================== +--- linux-5.4.24.orig/include/linux/platform_data/dsa.h ++++ linux-5.4.24/include/linux/platform_data/dsa.h +@@ -6,7 +6,7 @@ struct device; + struct net_device; + + #define DSA_MAX_SWITCHES 4 +-#define DSA_MAX_PORTS 12 ++#define DSA_MAX_PORTS 54 + #define DSA_RTABLE_NONE -1 + + struct dsa_chip_data { diff --git a/target/linux/rtl838x/patches-5.4/702-net-ethernet-add-support-for-rtl838x-ethernet.patch b/target/linux/rtl838x/patches-5.4/702-net-ethernet-add-support-for-rtl838x-ethernet.patch new file mode 100644 index 0000000000..11e62450d5 --- /dev/null +++ b/target/linux/rtl838x/patches-5.4/702-net-ethernet-add-support-for-rtl838x-ethernet.patch @@ -0,0 +1,26 @@ +--- a/drivers/net/ethernet/Kconfig ++++ b/drivers/net/ethernet/Kconfig +@@ -163,6 +163,13 @@ source "drivers/net/ethernet/rdc/Kconfig + source "drivers/net/ethernet/realtek/Kconfig" + source "drivers/net/ethernet/renesas/Kconfig" + source "drivers/net/ethernet/rocker/Kconfig" ++ ++config NET_RTL838X ++ tristate "Realtek rtl838x Ethernet MAC support" ++ depends on RTL838X ++ ---help--- ++ Say Y here if you want to use the Realtek rtl838x Gbps Ethernet MAC. ++ + source "drivers/net/ethernet/samsung/Kconfig" + source "drivers/net/ethernet/seeq/Kconfig" + source "drivers/net/ethernet/sfc/Kconfig" +--- a/drivers/net/ethernet/Makefile ++++ b/drivers/net/ethernet/Makefile +@@ -76,6 +76,7 @@ obj-$(CONFIG_NET_VENDOR_REALTEK) += real + obj-$(CONFIG_NET_VENDOR_RENESAS) += renesas/ + obj-$(CONFIG_NET_VENDOR_RDC) += rdc/ + obj-$(CONFIG_NET_VENDOR_ROCKER) += rocker/ ++obj-$(CONFIG_NET_RTL838X) += rtl838x_eth.o + obj-$(CONFIG_NET_VENDOR_SAMSUNG) += samsung/ + obj-$(CONFIG_NET_VENDOR_SEEQ) += seeq/ + obj-$(CONFIG_NET_VENDOR_SILAN) += silan/ diff --git a/target/linux/rtl838x/patches-5.4/703-include-linux-add-phy-ops-for-rtl838x.patch b/target/linux/rtl838x/patches-5.4/703-include-linux-add-phy-ops-for-rtl838x.patch new file mode 100644 index 0000000000..db8b0403f7 --- /dev/null +++ b/target/linux/rtl838x/patches-5.4/703-include-linux-add-phy-ops-for-rtl838x.patch @@ -0,0 +1,13 @@ +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -628,6 +628,10 @@ + struct ethtool_tunable *tuna, + const void *data); + int (*set_loopback)(struct phy_device *dev, bool enable); ++ int (*get_port)(struct phy_device *dev); ++ int (*set_port)(struct phy_device *dev, int port); ++ int (*get_eee)(struct phy_device *dev, struct ethtool_eee *e); ++ int (*set_eee)(struct phy_device *dev, struct ethtool_eee *e); + }; + #define to_phy_driver(d) container_of(to_mdio_common_driver(d), \ + struct phy_driver, mdiodrv) diff --git a/target/linux/rtl838x/patches-5.4/704-drivers-net-phy-eee-support-for-rtl838x.patch b/target/linux/rtl838x/patches-5.4/704-drivers-net-phy-eee-support-for-rtl838x.patch new file mode 100644 index 0000000000..5262be53c6 --- /dev/null +++ b/target/linux/rtl838x/patches-5.4/704-drivers-net-phy-eee-support-for-rtl838x.patch @@ -0,0 +1,41 @@ +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -1244,6 +1244,11 @@ + + /* If we have a PHY, configure the phy */ + if (pl->phydev) { ++ if (pl->phydev->drv->get_port && pl->phydev->drv->set_port) { ++ if(pl->phydev->drv->get_port(pl->phydev) != kset->base.port) { ++ pl->phydev->drv->set_port(pl->phydev, kset->base.port); ++ } ++ } + ret = phy_ethtool_ksettings_set(pl->phydev, &our_kset); + if (ret) + return ret; +@@ -1422,8 +1427,11 @@ + + ASSERT_RTNL(); + +- if (pl->phydev) ++ if (pl->phydev) { ++ if (pl->phydev->drv->get_eee) ++ return pl->phydev->drv->get_eee(pl->phydev, eee); + ret = phy_ethtool_get_eee(pl->phydev, eee); ++ } + + return ret; + } +@@ -1440,9 +1448,11 @@ + + ASSERT_RTNL(); + +- if (pl->phydev) ++ if (pl->phydev) { ++ if (pl->phydev->drv->set_eee) ++ return pl->phydev->drv->set_eee(pl->phydev, eee); + ret = phy_ethtool_set_eee(pl->phydev, eee); +- ++ } + return ret; + } + EXPORT_SYMBOL_GPL(phylink_ethtool_set_eee); diff --git a/target/linux/rtl838x/profiles/00-default.mk b/target/linux/rtl838x/profiles/00-default.mk new file mode 100644 index 0000000000..7f2053da55 --- /dev/null +++ b/target/linux/rtl838x/profiles/00-default.mk @@ -0,0 +1,14 @@ +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# + +define Profile/Default + NAME:=Default Profile + PRIORITY:=1 +endef + +define Profile/Default/Description + Default package set compatible with most boards. +endef + +$(eval $(call Profile,Default))