ZyXEL GS1900-10HP revision B1 support OpenWrt firmware?

I may have misunderstood something somewhere, but I thought, LUA was being deprecated in favor for UCODE? If so, do you have any plans catering to that? In any case, thanks already for your work!

Right now, Zyxel gs1900-10HP both revisions include realtek-poe as "device-package" in the default recipe. realtek-poe has an open PR for B1 rev support, that looks like it could use your help :slight_smile:

I have these entries in my /etc/config/poe:

config global
	option budget '77'

Is that what you were talking of? I won't run many devices anyway, so I don't worry much about the power limits. Just a couple of accesspoints. As long as I don't upgrade to wifi7, power is not a concern.

It's another global setting, in the config file it looks something like this:

config global
	option budget '77.0'
	option guard '11.0'
1 Like

In any case, thanks already for your work!

Of course, no problem. Happy to see feedback!

I thought, LUA was being deprecated in favor for UCODE?

Yes, my understanding too.

If so, do you have any plans catering to that?

I did briefly look into using UCode.

It would be just as good as Lua for rapidly prototyping stuff for new hardware and firmware, and/or upcoming PSE features.

ucode also gets rid of the requirement for prerequisite packages, which would be very nice. It has great uci integration too, and much better ubus integration.

What it does not have is non-blocking IO support. So you cannot fx. read from a UART with timeout. This is what synchronize() and exchange_frame() does in the code, when it talks to the controller.

There's also no way to do syscalls from ucode. Something that configure_uart() ends up doing in order to invoke an ioctl on /dev/ttyS1 to configure the baud rate and set that "terminal" to raw IO mode.

Rest of the code is easily rewritten in ucode. With above mentioned benefits.

To make a long story short, in order to implement the 90% of the code that is convertible into ucode, one would have to also build something like a small daemon in C, call it "poe_framer" or some such, that consists of configure_uart() + synchronize() + exchange_frame() in current code.

That daemon would handle UART setup and comms, frame sequence numbers + checksums, and then expose a device or FIFO to ucode.

A separate daemon in ucode that knows the particular PSE hardware/firmware specifics can then use that FIFO to exchange commands/replies with the controller (and use another FIFO to read any unsolicited messages from the controller).

The C daemon would reply on the FIFO with fx. the string "timeout" after eg. 10 seconds in order to unblock ucode, if an exchange with the controller fails, to reasonably elegantly get around the ucode blocking read behavior.

An advantage would be that the poe_framer daemon could talk to any I2C/UART controller that uses the 12-byte frame + sequence + checksum format. (Seems like this is the industry standard for PoE PSE chips, for whatever reason.)

The controller-specific / firmware-specific parts (different command opcodes, replies and different supported features) could be handled in a per-controller fashion in the ucode-based daemon, either with one codebase per hardware model, or however that ends up fleshing out; there's room to experiment. The C daemon would be the same for all models, needing just a couple command-line parameters to tell it which I2C/UART and baud to use and where to expose the controller FIFOs.

It's definitely doable, and arguably not a bad solution at all.

Buuut switching to ucode requires that little bit of C. And doing anything in C requires setting up buildroot, writing software, compiling into firmware, flashing to a test box, and iterating over that process. That stuff takes time, and I was only away for 2 days :-).

The switch I needed to get PoE ports up and running on was also in another country doing production stuff. So this way I avoided the risky business of messing with the firmware remotely too much.

TL;DR a flexible daemon in ucode on top of a tiny hardware abstraction layer in C looks possible, would probably be neat, and seems like a good classic design. Didn't go that direction mainly due to time constraints at the time...

realtek-poe has an open PR for B1 rev support

Yep, I saw that! Looks good. Never managed to actually download the build artifacts and try it out; maybe it works too? Those guys seem to have a really good handle on things. Never managed to actually download the build artifacts and try it out unfortunately.

There's also an older project called just "poemgr" which I have used with great success on "usw-flex" switches by Ubiquiti.

Gave your idea about using UCODE a bit more thought..

I think it's a good idea with UCODE, allows rapid development for new PSE controllers that come out. Also good for quickly iterating on supporting additional PSE features.

Perhaps I veered off on a tangent too much with the whole having-two-daemons idea :joy:

How about a simple library for talking between ucode and the pse instead? Call it "libupse" perhaps.

(Or "libupse_12osc", to clearly indicate that it's a .so for exchanging 12-byte frames containing opcode, sequence and checksum.)

UCODE would load the .so, yeet it a port name and a baud rate. The .so would kick off a thread that runs configure_uart(), synchronize() and then just loops over exchange_message().

Next step, the .so provides a couple of hooks for UCODE to talk to. Call it for example queue_cmd_and_await_reply() to inject a message into the loop that runs frame exchanges, and fetch_unsolicited() to grab any notifications from the PSE controller about changes to its steady state.

Gets rid of the need for a FIFO and a separate daemon. Keeps all the configuration in UCI and handled in one place in UCODE. Results in only a minimal amount of C code to maintain in the future.

I don't know if openwrt maintainers would be likely to allow an additional ucode library though. Maybe?

Also not sure how to go about coding it.. Long shot, but do you happen to know of any good developer docs about making .so libs that can be require()d from UCODE?

3 Likes

I think, JOW is the authoritative source on UCODE. Myself, I don't know much about it.

You may not have read about it yet, but Linux 6.10+ includes a POE framework, that then ties into ethtool, 6.11 contained further enhancements still, so I think, it is save to say, that all current solutions (your lua script, poed, poemgr and realtek-poe) will become obsolete when OpenWrt hops over to 6.12 or so.

In the interim, we have the following situation:

  • 2 HW versions of GS1900-10HP, only differing w.r.t. the POE solution
  • OpenWrt does not see them as different devices, and forces realtek-poe via Makefile for both
  • realtek-poe has an unreleased PR aiming to add support for Rev B1 POE solution. Not sure, if there are still functional problems, or just polishing left.
  • Your Lua solution, that seems to be functionally complete

I'd argue, that It might be most useful to (help) complete realtek-poe, where the chance is higher, that a more direct port of the c-logic will one day become part of the proper mainline upstream Linux solution, and in the short-term (the good ol' 6.6 days) we can properly use Rev B devices using the newer POE chip.

I'll be at hack.lu most days this week, so I wont have time to work on any of this during the next week.

1 Like

I think [...] current solutions (your lua script, poed, poemgr and realtek-poe) will become obsolete when OpenWrt hops over to 6.12 or so.

Interesting!

  • Your Lua solution, that seems to be functionally complete

It works great so far..

But I think there's still a few areas unexplored. Probably the controller sends a notification when it takes action on over-amperage for example. Would be nice to react to that somehow; currently poemgr-realtek just logs a "notification was received" in the form of a statistics counter.

Probably there's also a couple more features available. Fx. one to disable active port power negotiation and always passively deliver 802.3af mode B juice (for very old devices). Fx. another to read out more debug information about device detection and classification process.

And it needs an .ipk package instead of having to install 2 files manually on the device of course. :wink:

in the short-term (the good ol' 6.6 days) we can properly use Rev B devices using the newer POE chip.

Yep!

Yeah maybe I'll just revisit the UCODE idea if it turns out that the new OpenWRT release becomes too large to fit Lua on the devices, and if PR #25 remains open, since the current code works quite well already.

I'll be at hack.lu most days this week, so I wont have time to work on any of this during the next week.

Sounds fun, hope you have/had a good time!

Now that I think of it, poemgr-realtek should work on the Zyxel GS1900-24EP model switches too.

We need someone who owns that model to test it (with another modification to the boardmodel + pseid check). :thinking:

I have one and contributed the initial port - trying first steps with PoE management now and noticing the uboot env isn't immediately accessible, so that comes first I guess.

EDIT:

root@OpenWrt:~# fw_printenv -c /etc/fw_env.config 
Warning: Bad CRC, using default environment
bootcmd=run distro_bootcmd
bootdelay=2
baudrate=115200
loadaddr=0x0
mtdids=
mtdparts=
bootm_size=0x10000000
eth6addr=02:00:11:22:33:47
ethaddr=02:00:11:22:33:44
fdt_addr_r=0xc00000
ipaddr=192.0.2.1
kernel_addr_r=0x1000000
pxefile_addr_r=0x2000
ramdisk_addr_r=0x2000000
scriptaddr=0x1000
stderr=serial,vidconsole
stdin=serial
stdout=serial,vidconsole
root@OpenWrt:~# fw_printenv -c /etc/fw_sys.config 
resetdefault=0
pseId=4
mac_start=F4:4D:5C:6B:53:45
mac_end=F4:4D:5C:6B:53:5D
sn=S232L25000911
dualfname0=openwrt-realtek-rtl838x-zyxel_gs1900-24ep-initramfs-kernel.bin
1 Like
[..]
OpenWrt-libtool: compile:  mips-openwrt-linux-musl-gcc -DPACKAGE_NAME=\"librs232\" -DPACKAGE_TARNAME=\"librs232\" -DPACKAGE_VERSION=\"1.0.3\" "-DPACKAGE_STRING=\"librs232 1.0.3\"" -DPACKAGE_BUGREPORT=\"ynezz@true.cz\" -DPACKAGE_URL=\"\" -DPACKAGE=\"librs232\" -DVERSION=\"1.0.3\" -DHAVE_STDIO_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_STRINGS_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_UNISTD_H=1 -DSTDC_HEADERS=1 -DHAVE_DLFCN_H=1 -DLT_OBJDIR=\".libs/\" -DHAVE_LUA_H=1 -DHAVE_LUALIB_H=1 -DHAVE_LAUXLIB_H=1 -DHAVE_LUACONF_H=1 -DBUILD_LUA=1 -I. -I/home/mirko/openwrt.git/staging_dir/toolchain-mips_4kec_gcc-13.3.0_musl/usr/include -I/home/mirko/openwrt.git/staging_dir/toolchain-mips_4kec_gcc-13.3.0_musl/include -I/home/mirko/openwrt.git/staging_dir/toolchain-mips_4kec_gcc-13.3.0_musl/include/fortify -I../include -Wall -ansi -pedantic -W -Wmissing-prototypes -Wmissing-declarations -Werror -std=gnu99 -O2 -Os -pipe -mno-branch-likely -mips32r2 -mtune=4kec -fno-caller-saves -fno-plt -fhonour-copts -msoft-float "-fmacro-prefix-map=/home/mirko/openwrt.git/build_dir/target-mips_4kec_musl/lua-rs232-2019.11.20~c106c94d=lua-rs232-2019.11.20~c106c94d" -mips16 -minterlink-mips16 -ffunction-sections -fdata-sections -Wformat -Werror=format-security -fstack-protector -D_FORTIFY_SOURCE=1 -Wl,-z,now -Wl,-z,relro -c rs232_posix.c  -fPIC -DPIC -o .libs/rs232_posix.o
rs232_posix.c:490:1: error: conflicting types for 'rs232_set_baud' due to enum/integer mismatch; have 'unsigned int(struct rs232_port_t *, enum rs232_baud_e)' [-Werror=enum-int-mismatch]
  490 | rs232_set_baud(struct rs232_port_t *p, enum rs232_baud_e baud)
      | ^~~~~~~~~~~~~~
In file included from rs232_posix.c:39:
../include/librs232/rs232.h:203:24: note: previous declaration of 'rs232_set_baud' with type 'unsigned int(struct rs232_port_t *, unsigned int)'
  203 | RS232_LIB unsigned int rs232_set_baud(struct rs232_port_t *p, unsigned int baud);
      |                        ^~~~~~~~~~~~~~
rs232_posix.c:591:1: error: conflicting types for 'rs232_set_dtr' due to enum/integer mismatch; have 'unsigned int(struct rs232_port_t *, enum rs232_dtr_e)' [-Werror=enum-int-mismatch]
  591 | rs232_set_dtr(struct rs232_port_t *p, enum rs232_dtr_e state)
      | ^~~~~~~~~~~~~
../include/librs232/rs232.h:208:24: note: previous declaration of 'rs232_set_dtr' with type 'unsigned int(struct rs232_port_t *, unsigned int)'
  208 | RS232_LIB unsigned int rs232_set_dtr(struct rs232_port_t *p, unsigned int dtr);
      |                        ^~~~~~~~~~~~~
rs232_posix.c:632:1: error: conflicting types for 'rs232_set_rts' due to enum/integer mismatch; have 'unsigned int(struct rs232_port_t *, enum rs232_rts_e)' [-Werror=enum-int-mismatch]
  632 | rs232_set_rts(struct rs232_port_t *p, enum rs232_rts_e state)
      | ^~~~~~~~~~~~~
../include/librs232/rs232.h:209:24: note: previous declaration of 'rs232_set_rts' with type 'unsigned int(struct rs232_port_t *, unsigned int)'
  209 | RS232_LIB unsigned int rs232_set_rts(struct rs232_port_t *p, unsigned int rts);
      |                        ^~~~~~~~~~~~~
rs232_posix.c:673:1: error: conflicting types for 'rs232_set_parity' due to enum/integer mismatch; have 'unsigned int(struct rs232_port_t *, enum rs232_parity_e)' [-Werror=enum-int-mismatch]
  673 | rs232_set_parity(struct rs232_port_t *p, enum rs232_parity_e parity)
      | ^~~~~~~~~~~~~~~~
../include/librs232/rs232.h:206:24: note: previous declaration of 'rs232_set_parity' with type 'unsigned int(struct rs232_port_t *, unsigned int)'
  206 | RS232_LIB unsigned int rs232_set_parity(struct rs232_port_t *p, unsigned int parity);
      |                        ^~~~~~~~~~~~~~~~
rs232_posix.c:708:1: error: conflicting types for 'rs232_set_stop' due to enum/integer mismatch; have 'unsigned int(struct rs232_port_t *, enum rs232_stop_e)' [-Werror=enum-int-mismatch]
  708 | rs232_set_stop(struct rs232_port_t *p, enum rs232_stop_e stop)
      | ^~~~~~~~~~~~~~
../include/librs232/rs232.h:204:24: note: previous declaration of 'rs232_set_stop' with type 'unsigned int(struct rs232_port_t *, unsigned int)'
  204 | RS232_LIB unsigned int rs232_set_stop(struct rs232_port_t *p, unsigned int stop);
      |                        ^~~~~~~~~~~~~~
rs232_posix.c:739:1: error: conflicting types for 'rs232_set_data' due to enum/integer mismatch; have 'unsigned int(struct rs232_port_t *, enum rs232_data_e)' [-Werror=enum-int-mismatch]
  739 | rs232_set_data(struct rs232_port_t *p, enum rs232_data_e data)
      | ^~~~~~~~~~~~~~
../include/librs232/rs232.h:205:24: note: previous declaration of 'rs232_set_data' with type 'unsigned int(struct rs232_port_t *, unsigned int)'
  205 | RS232_LIB unsigned int rs232_set_data(struct rs232_port_t *p, unsigned int data);
      |                        ^~~~~~~~~~~~~~
rs232_posix.c:777:1: error: conflicting types for 'rs232_set_flow' due to enum/integer mismatch; have 'unsigned int(struct rs232_port_t *, enum rs232_flow_e)' [-Werror=enum-int-mismatch]
  777 | rs232_set_flow(struct rs232_port_t *p, enum rs232_flow_e flow)
      | ^~~~~~~~~~~~~~
../include/librs232/rs232.h:207:24: note: previous declaration of 'rs232_set_flow' with type 'unsigned int(struct rs232_port_t *, unsigned int)'
  207 | RS232_LIB unsigned int rs232_set_flow(struct rs232_port_t *p, unsigned int flow);
      |                        ^~~~~~~~~~~~~~
cc1: all warnings being treated as errors
make[4]: *** [Makefile:520: rs232_posix.lo] Error 1

sigh

root@OpenWrt:~# /tmp/poe-mgr 
configured device: /dev/ttyS1, baud: 115200, data bits: 8, parity: none, stop bits: 1, flow control: off
wait for quiescent /dev/ttyS1
controller status request ok, 16 ports
timeout while waiting for response
timeout while waiting for response
Connection to 10.254.254.5 closed.

:wink:

1 Like

Aweeesomeee. Thank you!

Completely missed that these devices have both "u-boot-env" and "u-boot-env2" partitions. Oops.

fw_printsys outputs exactly the same here (except I don't have "dualfname0").

Entirely different fw_env though:

root@OpenWrt:~# fw_printenv
HTPLog=1
baudrate=115200
boardmodel=ZyXEL_GS1900_10HPv2
bootargs=console=ttyS0,115200 mem=64M quiet
bootcmd=boota
bootdelay=0
ethact=rtl8380#0
ethaddr=F4:4D:5C:70:54:C7
ipaddr=192.168.1.1
netmask=255.255.255.0
pseId=4
runHTP=0
sn=S242L09001234
stderr=serial
stdin=serial
stdout=serial

Could the "Bad CRC, using default environment" warning have something to do with why yours is missing eg. boardmodel and pseId in fw_env?

What happens if you run:

root@OpenWrt:~# egrep -o \\w+=.+ /dev/mtd1

Does it spit something out containing eg. boardmodel=<blah>?

Kinda makes me want to just rip the whole board mismatch detection (and thus fw_printenv dependency) out of poemgr-realtek...

(Code looks at "boardmodel" because it's currently the only way to discern gs1900-10hpv2 mainboards. Maybe v2 mainboards should have their own DTS so one could get the board revision from /etc/board.json instead, hmm.)

Welp, there's as good an argument as any for dropping Lua at some point. :melting_face:

Looks the same in github.com/ynezz/librs232 repository. Perhaps meant to be compiled without -Werror=enum-int-mismatch? :man_shrugging:

Looks stellar so far, the initial status command received a succesful reply.

Harumph, slightly less awesome.

If you have time, could you run /tmp/poe-mgr again with "-d" and "-v" command line options added?

I'd like to see which command fails and if anything at all is received on the uart.

egrep -o \w+=.+ /dev/mtd1

root@OpenWrt:~# egrep -o \\w+=.+ /dev/mtd1
tHTPLog=0
baudrate=115200
boardmodel=ZyXEL_GS1900_24EP
bootargs=console=ttyS0,115200 mem=64M quiet
bootcmd=boota
bootdelay=1
ethact=rtl8380#0
ethaddr=F4:4D:5C:6B:53:45
ipaddr=192.168.1.1
netmask=255.255.255.0
runHTP=0
serverip=192.168.1.111
stderr=serial
stdin=serial
stdout=serial

-Werror=enum-int-mismatch

Yeah, the following diff did the job for now:

diff --git a/lang/lua-rs232/Makefile b/lang/lua-rs232/Makefile
index 654e21c..a4b6642 100644
--- a/lang/lua-rs232/Makefile
+++ b/lang/lua-rs232/Makefile
@@ -41,6 +41,7 @@ define Package/lua-rs232/description
   multiplatform library for serial communications over RS-232
 endef
 
+TARGET_CFLAGS += -Wno-error=enum-int-mismatch
 TARGET_LDFLAGS += -Wl,--as-needed
 MAKE_FLAGS += luadir='$$$${prefix}/lib/lua' luaexecdir='$$$${exec_prefix}/lib/lua'

Could the "Bad CRC, using default environment" warning have something to do with why yours is missing eg. boardmodel and pseId in fw_env?

Very likely it's just looking at the wrong place and as a fallback printing compiled-in default data, maybe not even from uboot itself but the userspace tools.

If you have time, could you run /tmp/poe-mgr again with "-d" and "-v" command line options added?

I'm using this switch currently productively, so will do once I have time to recovery from what might go wrong.
What probably wasn't clear from my previous output: the switch became pretty much unusable while running the lua script. It still responded to ICMP but everything userspace related just timed-out, incl. the SSH session.

1 Like

Probably wrong size configured for the u-boot env block. All the other Zyxel switches use 0x400 for mtd1 and 0x1000 for mtd2. But your model is not known so it gets the realtek defauls which are 0x10000 for mtd1 and 0x1000 for mtd2. Which explains why your fw_sys.config works.

See package/boot/uboot-envtools/files/realtek

And try changing /etc/fw_env.config from

/dev/mtd1 0x0 0x10000 0x10000 

to

/dev/mtd1 0x0 0x400 0x10000 
4 Likes

Exciting!

Also, as Ralph would say, 'unpossible'... :thinking:

I looked at the code again, there is nothing in there to busy-loop the CPU as far as I can tell.

The main frame exchange just does a select(..., timeout) and then a read(fd, ...), neither should consume CPU. There's also a retry loop around that for short reads but it has a hard limit of 12 rounds max.

Hmm. Maybe some other process is using /dev/ttyS1 and gets confused when poemgr-realtek talks to it ??

In any case, I added some minor improvements:

  • Add boardmodel=ZyXEL_GS1900_24EP
  • Add -f option to skip hardware compatibility check entirely (eg. if /dev/mtd1 is borked, or for testing)
  • Add one more assert() just in case

Here:

/usr/bin/poemgr-realtek:

#!/usr/bin/lua

-------------------------------------------------------------
----  For testing, start in foreground with:
----  $ poemgr-realtek -d -v
----
----  ("-d" is debug, "-v" is verbose)
----
----  While the daemon is running, PoE ports can be manually
----  enabled via ubus:
----  $ ubus call poe manage "{'port':'lan1', 'enable':true}"
----
----  Inspect the per-port consumption to see if power is delivered:
----  $ ubus call poe info
----
----  If everything works, install the PoE manager:
----    1. poe manager (this file)   --> /usr/bin/poemgr-realtek
----    2. service definition file   --> /etc/init.d/poemgr-realtek
----    3. configuration             --> /etc/config/poe
----
----  And install required packages / dependencies:
----  $ opkg install lua-rs232 libuci-lua libubus-lua
----
----  Start the service via GUI (System -> Startup) or CLI:
----  $ service poemgr-realtek start
----
----  In the configuration file, PoE ports can be enabled or
----  disabled with the "enable" property.
----
----  Priority groups can be configured with "priority".
----  Priorities range from 0 (low) to 3 (critical), with
----  1 (normal) and 2 (high) in between.
----
----  Mapping between the PSE power lines and Ethernet ports
----  can be adjusted with the "id" and "name" properties.
----
----  After modifying the configuration file or updating
----  configuration via UCI, perform a commit to push
----  changes to the PoE controller:
----  $ ubus call uci commit '{"config":"poe"}'
----
----  For reading out internal statistics, the following
----  command is available.
----  $ ubus call poe stats
----
----  For experimenting with new or updated opcodes in the
----  controller firmware, the following command is available.
----  $ ubus call poe sendframe '{"frame":"40"}'
-------------------------------------------------------------

require "uci"
require "ubus"
require "uloop"

local rs = require "luars232"
local stderr = io.stderr

-- PSE known power limits for smallest supported device (zyxel,gs1900-10hp-v2)
local PSE_WATTS = 77.0
local PSE_GUARD = 11.0

-- SoC serial port for talking to MCU
local SOC_UART = "/dev/ttyS1"

-- MCU baud rate (Realtek RTL8238)
local MCU_RATE = rs.RS232_BAUD_115200

-- How many times to try again in case of timeouts, checksum errors etc.
local MAX_RETRIES = 3

-- MCU error codes (Realtek RTL8238)
local MCU_ERR_CHECKSUM = 0x62
local MCU_ERR_INCOMPLETE = 0xfd
local MCU_ERR_MALFORMED = 0xfe
local MCU_ERR_BUSY = 0xff

-- MCU commands (Realtek RTL8238)
local MCU_CMD_GET_CONTROLLER_STATUS = 0x40
local MCU_CMD_SET_GLOBAL_POWER_LIMITS = 0x04
local MCU_CMD_GET_GLOBAL_POWER_USAGE = 0x41
local MCU_CMD_SET_PORT_PRIORITY = 0x15
local MCU_CMD_SET_PORT_POWER_DELIVERY = 0x01
local MCU_CMD_GET_PORT_POWER_USAGE = 0x44

-- Hardware the above is tested on
local MATCH_BOARDNAME_1 = "ZyXEL_GS1900_10HPv2"
local MATCH_BOARDNAME_2 = "ZyXEL_GS1900_24EP"
local MATCH_PSEID_1 = "4"
local MATCH_PSEID_2 = ""

-- Text markings on packages - guesswork, need photos
local CHIPNAME_PSE = "Nuvoton NUC029ZAN"
local CHIPNAME_MCU = "Realtek RTL8238"

-- Internal statistics counters
local stats_msg = 0
local stats_timeout = 0
local stats_checksum = 0
local stats_error = 0
local stats_notify = 0
local stats_reload = 0

-- Command-line options
local skip_hwcheck = false
local log_verbose = false
local log_debug = false

-- Filled with defaults (1=lan1, 2=lan2 etc.) during init
local ifname
local portid, portprio, portenable
local budget, guard
local poe_ports

-- Globals
local seq = 0
local queue = {}

function verbose(s)
	if log_verbose then io.write(s) end
end

function debug(s)
	if log_debug then io.write(s) end
end

function check(cond, msg)
	if not cond then
		if type(msg) == "function" then msg = msg() end
		stderr:write(msg)
		assert(false)
	end
end

function read_fw(tool, key)
	local handle = io.popen(string.format("%s -n %s", tool, key))
	local output = handle:read("*a")
	local res = output:gsub('[\n\r]', '')
	handle:close()
	return res
end

function read_fw_env(key)
	return read_fw("fw_printenv", key)
end

function read_fw_sys(key)
	return read_fw("fw_printsys", key)
end

function check_compatible()
	if skip_hwcheck then
		verbose("skip board revision check\n")
	else
		local model = read_fw_env("boardmodel")
		local pseid = read_fw_sys("pseId")
		check(
			(model == MATCH_BOARDNAME_1 or model == MATCH_BOARDNAME_2) and (pseid == MATCH_PSEID_1 or pseid == MATCH_PSEID_2),
			"ERROR: unknown hardware model or revision\n"
		)
	end
end

function configure_uart()
	local err, handle = rs.open(SOC_UART)
	check(err == rs.RS232_ERR_NOERROR, function() return string.format("ERROR: failed to open('%s'): %s\n", SOC_UART, rs.error_tostring(err)) end)

	check(handle:set_baud_rate(MCU_RATE) == rs.RS232_ERR_NOERROR, "ERROR: failed to set baud rate")
	check(handle:set_data_bits(rs.RS232_DATA_8) == rs.RS232_ERR_NOERROR, "ERROR: failed to set data bits")
	check(handle:set_parity(rs.RS232_PARITY_NONE) == rs.RS232_ERR_NOERROR, "ERROR: failed to set parity")
	check(handle:set_stop_bits(rs.RS232_STOP_1) == rs.RS232_ERR_NOERROR, "ERROR: failed to set stop bits")
	check(handle:set_flow_control(rs.RS232_FLOW_OFF)  == rs.RS232_ERR_NOERROR, "ERROR: failed to disable flow control")

	io.write(string.format("configured %s\n", tostring(handle)))
	synchronize(handle)
	return handle
end

function synchronize(handle)
	local interval = 1000
	local buffer = {}
	local size = 1
	local err, data, i

	io.write(string.format("wait for quiescent %s\n", SOC_UART))
	while size > 0 do
		err, data, size = handle:read(4096, interval)
		if err == rs.RS232_ERR_NOERROR then
			for i = 1, string.len(data) do
				table.insert(buffer, string.byte(string.sub(data, i, i)))
			end
		elseif err == rs.RS232_ERR_TIMEOUT then
			size = 0
		else
			check(false, string.format("ERROR: failed to read(): %s\n", rs.error_tostring(err)))
		end
	end

	if #buffer > 0 then
		debug("RX")
		for i = 1, #buffer do
			debug(string.format(" %02x", buffer[i]))
		end
		debug("\n")
	end
end

function try_process_unsolicited()
	-- handle any notifications received asynchronously
	stats_notify = stats_notify + #queue
	if #queue == 0 then return nil end
	verbose(string.format("ignoring %d unknown notifications from controller\n", #queue))
	queue = {}
end

function try_exchange_message(h, xmit)
	local interval = 1000
	local retries = 10
	local recv = {}
	local round = 0
	local sum = 0
	local data = ""
	local err, size, left, i

	if #xmit > 12 then
		verbose(string.format("output frame too large, discard\n", xmit))
		return nil
	end

	if #xmit < 1 then
		verbose("output frame too small, discard\n")
		return nil
	end

	-- reserve all-zeroes and bit 2^7 for controller
	seq = (seq % 127) + 1
	xmit[2] = seq

	-- pad to full frame length
	while #xmit < 12 do
		table.insert(xmit, 0xff)
	end

	-- coalesce to zero to avoid crash on invalid input
	for i = 1, 12 do
		if type(xmit[i]) ~= "number" then xmit[i] = 0 end
		xmit[i] = math.floor(math.abs(xmit[i])) % 256
	end

	-- calculate checksum
	for i = 1, 11 do
		sum = sum + xmit[i]
	end
	xmit[12] = sum % 256

	debug("TX ->")
	for i = 1, 12 do
		debug(string.format(" %02x", xmit[i]))
	end
	debug("\n")

	for i = 1, 12 do
		data = data .. string.char(xmit[i])
	end

	err, size = h:write(data)
	check(err == rs.RS232_ERR_NOERROR, function() return string.format("ERROR: failed to write(): %s\n", rs.error_tostring(err)) end)

	while round < retries do
		while round < retries and #recv < 12 do
			err, data, size = h:read(12 - #recv, interval)
			if err == rs.RS232_ERR_NOERROR then
				check(#recv + size < 13, "ERROR: read() returned more octets than asked")
				check(size > 0, "ERROR: RX buffer emptied between select() and read()")
				for i = 1, string.len(data) do
					table.insert(recv, string.byte(string.sub(data, i, i)))
				end
			elseif err ~= rs.RS232_ERR_TIMEOUT then
				check(false, string.format("ERROR: failed to read(): %s\n", rs.error_tostring(err)))
			end
			round = round + 1
		end

		if #recv > 0 then
			stats_msg = stats_msg + 1
			debug("RX <-")
			for i = 1, #recv do
				debug(string.format(" %02x", recv[i]))
			end
			debug("\n")
		end

		if #recv < 12 then
			stats_timeout = stats_timeout + 1
			stderr:write("timeout while waiting for response\n")
			return nil
		end

		sum = 0
		for i = 1, 11 do
			sum = sum + recv[i]
		end

		if sum % 256 ~= recv[12] then
			stats_checksum = stats_checksum + 1
			stderr:write("frame checksum invalid in response\n")
			return nil

		elseif recv[1] ~= xmit[1] and recv[2] ~= xmit[2] then
			verbose("unsolicited frame, add to queue\n");
			table.insert(queue, recv)
			recv = {}
			round = 0

		elseif recv[2] ~= xmit[2] then
			recv = {}
			verbose("wrong sequence number from controller, discard\n")

		elseif recv[1] == MCU_ERR_CHECKSUM then
			stats_error = stats_error + 1
			verbose("controller report: invalid checksum\n")

		elseif recv[1] == MCU_ERR_INCOMPLETE then
			stats_error = stats_error + 1
			verbose("controller report: incomplete request\n")

		elseif recv[1] == MCU_ERR_MALFORMED then
			stats_error = stats_error + 1
			verbose("controller report: malformed request\n")

		elseif recv[1] == MCU_ERR_BUSY then
			stats_error = stats_error + 1
			verbose("controller report: busy\n")

		elseif recv[1] ~= xmit[1] then
			stats_error = stats_error + 1
			verbose(string.format("controller report: (unknown error reply %s)\n", recv[1]));

		else
			return recv
		end
	end
end

function exchange_message_once(h, xmit)
	local recv = try_exchange_message(h, xmit)
	try_process_unsolicited()
	return recv
end

function exchange_message(h, xmit)
	local recv, round
	for round = 1, MAX_RETRIES do
		if round > 1 then verbose("Retrying...\n") end
		recv = exchange_message_once(h, xmit)
		if recv ~= nil then break end
	end
	return recv
end

function uintbe16(nr)
	-- coalesce to zero to avoid crash on malformed input
	if type(nr) ~= "number" then nr = 0 end
	local l = math.floor(nr * 10 / 256)
	local r = math.floor(nr * 10) % 256
	return l, r
end

function get_controller_status(h)
	verbose("request controller status\n")
	local cmd = {MCU_CMD_GET_CONTROLLER_STATUS}
	local reply = exchange_message(h, cmd)
	if type(reply) == "nil" then return nil end
	table.remove(reply, #reply)
	table.remove(reply, 1)
	table.remove(reply, 1)
	return unpack(reply)
end

function set_power_limits(h, total, guard)
	verbose(string.format("set power limits, global budget: %s, port guard: %s\n", total, guard))
	local cmd = {MCU_CMD_SET_GLOBAL_POWER_LIMITS, 0, 0x00}
	cmd[4], cmd[5] = uintbe16(total)
	cmd[6], cmd[7] = uintbe16(guard)
	exchange_message(h, cmd)
end

function get_global_power_usage(h)
	verbose("get global power usage\n")
	local cmd = {MCU_CMD_GET_GLOBAL_POWER_USAGE, 0}
	local reply = exchange_message(h, cmd)
	if type(reply) == "nil" then return nil end
	local watts = (reply[3] * 256 + reply[4]) / 10.0
	verbose(string.format("global watts: %s\n", watts))
	return watts
end

function set_port_priority(h, port, prio)
	verbose(string.format("set priority %s for port %s\n", prio, port))
	local cmd = {MCU_CMD_SET_PORT_PRIORITY, 0, port, prio}
	exchange_message(h, cmd)
end

function disable_port_power(h, port)
	verbose(string.format("disable power on port %s\n", port))
	local cmd = {MCU_CMD_SET_PORT_POWER_DELIVERY, 0, port, 0x00}
	exchange_message(h, cmd)
end

function enable_port_power(h, port)
	verbose(string.format("enable power on port %s\n", port))
	local cmd = {MCU_CMD_SET_PORT_POWER_DELIVERY, 0, port, 0x01}
	exchange_message(h, cmd)
end

function get_port_power_usage(h, port)
	verbose(string.format("get power usage on port %s\n", port))
	local cmd = {MCU_CMD_GET_PORT_POWER_USAGE, 0, port}
	local reply = exchange_message(h, cmd)
	if type(reply) == "nil" then return nil end
	local watts = (reply[10] * 256 + reply[11]) / 10.0
	local milliamps = reply[6] * 256 + reply[7]
	verbose(string.format("port %s watts: %s milliAmps: %s\n", port, watts, milliamps))
	return watts, milliamps
end

function count_ports(h)
	local ports = ({get_controller_status(h)})[2]
	check(type(ports) ~= "nil", "ERROR: failed to get controller global state\n")
	io.write(string.format("controller status request ok, %s ports\n", tostring(ports)))
	poe_ports = ports
end

function ensure_defaults()
	check(type(poe_ports) ~= "nil", "ERROR: port count not retrieved yet\n")
	-- default available pse port ids
	portid = {}
	local i
	for i = 1, poe_ports do
		table.insert(portid, i)
	end
	-- default global policy
	budget = PSE_WATTS
	guard = PSE_GUARD
	-- default pse port id to interface name mapping is lan1->pse1, lan2->pse2 etc.
	ifname = {}
	for i = 1, poe_ports do
		ifname[i] = "lan" .. tostring(i)
	end
	-- default is empty, so to do nothing
	portprio = {}
	portenable = {}
end

function load_config()
	-- preferred if each poe controller had its own
	-- top-level section (fx. "poe.poectrl0") but alas
	local cfg = uci.cursor()
	local k, v

	local global_budget
	local global_guard
	cfg:foreach("poe", "global", function(s)
		for k, v in pairs(s) do
			if k == "budget" then global_budget = tonumber(v) end
			if k == "guard" then global_guard = tonumber(v) end
		end
	end)
	-- if user supplied budget and/or guard values exist, apply
	if type(global_budget) == "number" then budget = global_budget end
	if type(global_guard) == "number" then guard = global_guard end

	verbose("loaded global policy")
	if type(global_budget) == "number" then verbose(" [budget]") end
	if type(global_guard) == "number" then verbose(" [guard]") end
	verbose("\n")

	local custom_ifname = {}
	local custom_enable = {}
	local custom_prio = {}
	local custom_portid = {}
	cfg:foreach("poe", "port", function(s)
		local p_id
		local p_prio
		local p_enable
		local p_name
		for k, v in pairs(s) do
			if k == "id" then p_id = tonumber(v) end
			if k == "priority" then p_prio = tonumber(v) end
			if k == "enable" then p_enable = tostring(v) end
			if k == "name" then p_name = tostring(v) end
		end
		if type(p_id) == "number" then
			table.insert(custom_portid, p_id)
			if type(p_prio) == "number" then custom_prio[p_id] = p_prio end
			if type(p_enable) == "string" then
				custom_enable[p_id] = (p_enable ~= "0" and p_enable ~= "false" and p_enable ~= "off" and p_enable ~= "no")
			end
			if type(p_name) == "string" then custom_ifname[p_id] = p_name end
		end
	end)
	-- user supplied pse port id values exist, apply
	if #custom_portid > 0 then portid = custom_portid end
	-- user supplied pse port priorities exist, apply
	if #custom_prio > 0 then portprio = custom_prio end
	-- user supplied pse port enable exist, apply
	if #custom_enable > 0 then portenable = custom_enable end
	-- user supplied eth<->pse interface name/id mapping exist, apply
	if #custom_ifname > 0 then ifname = custom_ifname end

	verbose("loaded per-port policy")
	if #custom_portid > 0 then verbose(" [port-ids: " .. tostring(#custom_portid) .. "]") end
	if #custom_prio > 0 then verbose(" [port-prios: " .. tostring(#custom_prio) .. "]") end
	if #custom_enable > 0 then verbose(" [port-enables: " .. tostring(#custom_enable) .. "]") end
	if #custom_ifname > 0 then verbose(" [port-idnames: " .. tostring(#custom_ifname) .. "]") end
	verbose("\n")

	stats_reload = stats_reload + 1
end

function apply_config(h)
	-- set global policy (budget etc.)
	set_power_limits(h, budget, guard)
	-- set policy for each port (priority etc.)
	local port, prio
	for port, prio in pairs(portprio) do
		set_port_priority(h, port - 1, prio)
	end
end

function run_administrative_actions(h)
	-- enable and disable individual ports
	local port, enable
	for port, enable in pairs(portenable) do
		local bit = enable ~= 0
		if bit then
			enable_port_power(h, port - 1)
		else
			disable_port_power(h, port - 1)
		end
	end
end

function parse_cmdline()
	local i
	for i = 1, #arg do
		if arg[i] == "-v" then log_verbose = true end
		if arg[i] == "-d" then log_debug = true end
		if arg[i] == "-f" then skip_hwcheck = true end
	end
end

-- adjust verbosity etc.
parse_cmdline()
-- quit early if wrong hardware
check_compatible()
-- make sure we can talk to the MCU
local h = configure_uart()
-- grab a port count used to build defaults
count_ports(h)
-- configure final bits and pieces of configurable settings
ensure_defaults()
-- load user config
load_config()
-- apply loaded configuration
apply_config(h)
-- finally, after applying policies, enable and disable power on the ports
run_administrative_actions(h)

uloop.init()

local conn = ubus.connect()
check(conn, "ERROR: could not connect to ubus\n")

local methods = {
	-- could register as eg. poe[0] in case of multiple controllers
	poe = {
		reload = {
			function(req, msg)
				ensure_defaults()
				load_config()
				apply_config(h)
				run_administrative_actions(h)
			end, {}
		},
		stats = {
			function(req, msg)
				local reply = {}
				reply['configuration reloads'] = stats_reload
				reply['messages exchanged'] = stats_msg
				reply['communication timeouts'] = stats_timeout
				reply['frame checksum errors'] = stats_checksum
				reply['notifications received'] = stats_notify
				reply['controller errors'] = stats_error
				conn:reply(req, reply)
			end, {}
		},
		sendframe = {
			function(req, msg)
				local reply = {}
				local hex = tostring(msg.frame)
				if type(hex) ~= "string" then hex = "" end
				hex = string.gsub(hex, '[^%x]', '')
				local xmit = {}
				for k in string.gmatch(hex, "(%x%x)") do table.insert(xmit, tonumber(k, 16)) end
				local recv = exchange_message_once(h, xmit)
				if type(recv) ~= "nil" then reply.frame = string.gsub(table.concat(recv, ' '), '(%d+)', function(k) return string.format('%02x', k) end) end
				conn:reply(req, reply)
			end, {frame = ubus.STRING}
		},
		info = {
			function(req, msg)
				local reply = {}
				local mcu_usage = get_global_power_usage(h)

				-- static values
				reply.mcu = CHIPNAME_MCU
				reply.pse = CHIPNAME_PSE

				-- values applied during last config reload
				reply.budget = string.format("%.1f", budget)
				reply.guard = string.format("%.1f", guard)

				-- values from controller
				reply.poe_ports = poe_ports
				-- seem to report about 4x too high ?
				reply.consumption = string.format("%.1f", mcu_usage)

				reply.ports = {}
				local i
				for i = 1, #portid do
					local item = {}
					local id = portid[i]
					local name = ifname[id]
					local prio = portprio[id]
					local enable = portenable[id]
					local w, ma = get_port_power_usage(h, id - 1)

					-- values applied during last config reload
					if type(name) ~= "nil" then item.name = name end
					if type(prio) ~= "nil" then item.priority = prio end
					if type(enable) ~= "nil" then
						if enable then item.enabled = "yes" else item.enabled = "no" end
					end

					-- values from controller
					item.consumption = string.format("%.1f", w)
					-- figure out command to fetch these
					--item.status = "disable" or "search" or "deliver" or "unknown"
					--item.mode = "poe++" or "poe+" or "poe", alt. "802.3bt" or "802.3at" or "802.3af"

					table.insert(reply.ports, item)
				end
				conn:reply(req, reply)
			end, {}
		},
		manage = {
			function(req, msg)
				local enable = msg.enable
				local portname = msg.port
				if type(enable) == "boolean" and type(portname) == "string" then
					local i
					for i = 1, #portid do
						local id = portid[i]
						local name = ifname[id]
						if name == portname then
							portenable[id] = enable
							if enable then
								enable_port_power(h, id - 1)
							else
								disable_port_power(h, id - 1)
							end
						end
					end
				end
			end, {port = ubus.STRING, enable = ubus.BOOLEAN}
		},
	},
}

conn:add(methods)

uloop.run()

/etc/init.d/poemgr-realtek:

#!/bin/sh /etc/rc.common

START=80
USE_PROCD=1
PROG=/usr/bin/poemgr-realtek

reload_service() {
	ubus call poe reload
}

service_triggers() {
	procd_add_config_trigger "config.change" "poe" ubus call poe reload
}

start_service() {
	procd_open_instance
	procd_set_param command "$PROG"
	procd_set_param respawn
	procd_set_param stdout 1
	procd_set_param stderr 1
	procd_close_instance
}

Run manually with these options (instead of enabling the service) to get debug output and skip the hardware check:

root@OpenWrt:~# /usr/bin/poemgr-realtek -v -d -f
1 Like

1st run:

root@OpenWrt:~# /tmp/poe-mgr -v -d -f
skip board revision check
configured device: /dev/ttyS1, baud: 115200, data bits: 8, parity: none, stop bits: 1, flow control: off
wait for quiescent /dev/ttyS1
request controller status
TX -> 40 01 ff ff ff ff ff ff ff ff ff 38
RX <- 17 02 61 ff ff ff ff ff
timeout while waiting for response
Retrying...
TX -> 40 02 ff ff ff ff ff ff ff ff ff 39
RX <- 40 02 ff 10 01 01 38 10
timeout while waiting for response
Retrying...
TX -> 40 03 ff ff ff ff ff ff ff ff ff 3a
RX <- 40 03 ff 10 01 01 38 10 02 03 00 ff
frame checksum invalid in response
ERROR: failed to get controller global state
/usr/bin/lua: /tmp/poe-mgr:130: assertion failed!

2nd run:

root@OpenWrt:~# /tmp/poe-mgr -v -d -f
skip board revision check
configured device: /dev/ttyS1, baud: 115200, data bits: 8, parity: none, stop bits: 1, flow control: off
wait for quiescent /dev/ttyS1
request controller status
TX -> 40 01 ff ff ff ff ff ff ff ff ff 38
RX <- 40 01 ff 10 01 01 38 10 18 04 00 ff
frame checksum invalid in response
Retrying...
TX -> 40 02 ff ff ff ff ff ff ff ff ff 39
RX <- 10 06 01 ff ff ff ff ff 1a 07 61 ff
frame checksum invalid in response
Retrying...
TX -> 40 03 ff ff ff ff ff ff ff ff ff 3a
RX <- ff ff ff ff 1c 08 61 ff ff ff ff ff
frame checksum invalid in response
ERROR: failed to get controller global state
/usr/bin/lua: /tmp/poe-mgr:130: assertion failed!

Third run:

root@OpenWrt:~# /tmp/poe-mgr -v -d
configured device: /dev/ttyS1, baud: 115200, data bits: 8, parity: none, stop bits: 1, flow control: off
wait for quiescent /dev/ttyS1
request controller status
TX -> 40 01 ff ff ff ff ff ff ff ff ff 38
RX <- ff ff ff 12 03 00 ff 24 1c 0c 61 ff
frame checksum invalid in response
Retrying...
TX -> 40 02 ff ff ff ff ff ff ff ff ff 39
RX <- 15 0e 04 00 05 00 06 00 ff ff ff 82
frame checksum invalid in response
Retrying...
TX -> 40 03 ff ff ff ff ff ff ff ff ff 3a
RX <- 1c 10 61 ff ff ff ff ff 11 11 ff ff
frame checksum invalid in response
ERROR: failed to get controller global state
/usr/bin/lua: /tmp/poe-mgr:130: assertion failed!

Fourth run:

root@OpenWrt:~# /tmp/poe-mgr -v -d
configured device: /dev/ttyS1, baud: 115200, data bits: 8, parity: none, stop bits: 1, flow control: off
wait for quiescent /dev/ttyS1
request controller status
TX -> 40 01 ff ff ff ff ff ff ff ff ff 38
RX <- 40 01 ff 10 01 01 38 10
timeout while waiting for response
Retrying...
TX -> 40 02 ff ff ff ff ff ff ff ff ff 39
RX <- 00 13 00 ff ff ff ff ff
timeout while waiting for response
Retrying...
TX -> 40 03 ff ff ff ff ff ff ff ff ff 3a
RX <- 00 16 01 ff ff ff ff ff 00 17 01 ff
frame checksum invalid in response
ERROR: failed to get controller global state
/usr/bin/lua: /tmp/poe-mgr:130: assertion failed!

The last run resulted in all PoE-ports cutting power. Maybe that was also the reason in my previously described "freeze"-experience, as it cuts off my PoE-WiFi-AP - however I remember ICMP-pings still worked..

Can you post a ps dump too:

root@OpenWrt:~# ps ww

Here are my first impressions..

Looks like this could be a reply, but not to the status request (40):

I think 17 is a valid opcode, but poemgr-realtek doesn't use it.

And we are only at sequence number 1 at this point, but the reply is to a command with sequence number 2.

Also:

Last 4 bytes are missing. I've seen these controllers send the first 8 octets first, then a little pause, then the last 4 octets (perhaps due to a FIFO buffer).

In this case the last 4 bytes have disappeared entirely, at least we didn't manage to catch them within the ~12 second window where we listen for a reply.

Did something else send an opcode 17 / sequence 02 command?

Did we fetch the first 8 bytes of that corresponding reply, then something else fetched the last 4 bytes?

Hmm.

Looks like a reply to the status request, good.

I've seen the controller take a little while between sending the first 8 bytes and the last 4, but not the 10+ seconds we are seeing here.

So seemingly 4 bytes disappeared (again?)...

Looks good! Except for missing checksum "ff".

Did checksums from the controller get turned off somehow?

(Easy enough to disable checksum validation in code, but probably preferable to get the controller to send a valid checksum.)

Reply looks good! Except for missing checksum "ff".

Not a reply to something we sent; sequence number is off (06 != 02) and opcode is different (10 != 40). (Not sure what opcode 10 is on this chip.)

I haven't seen the controller send a sequence number for its own notifications before (06 in the above), and wouldn't entirely rule it out, but..

Perhaps more likely this is a reply to a command we never sent?

Also:

It's possible that the last 4 bytes disappeared again, and what we are seeing at the end here (1a 07 61 ff) is the beginning of a new reply (to a command with opcode 1a and sequence number 07, which we again never sent, but alas).

"ff" is MCU_ERR_BUSY, blank sequence number, could be a controller-originated notification.

But if you squint a little and pretend that the first 4 bytes belong to a previous message (that had 8 bytes disappear on us at the front), then 1c 08 61 etc. could look like the beginning of a reply to a command with opcode 1c and sequence 08.

(Which we never sent.)

however I remember ICMP-pings still worked..

Ping is magical like that sometimes?.. :upside_down_face:

dmesg:

[    0.000000] Linux version 6.6.58 (mirko@openwrt) (mips-openwrt-linux-musl-gcc (OpenWrt GCC 13.3.0 r27965-57572de43a) 13.3.0, GNU ld (GNU Binutils) 2.42) #0 Tue Oct 29 11:00:53 2024
[    0.000000] RTL838X model is 83826800
[    0.000000] SoC Type: RTL8382
[    0.000000] printk: bootconsole [early0] enabled
[    0.000000] CPU0 revision is: 00019070 (MIPS 4KEc)
[    0.000000] MIPS: machine is Zyxel GS1900-24EP Switch
[    0.000000] earlycon: ns16550a0 at MMIO 0x18002000 (options '115200n8')
[    0.000000] printk: bootconsole [ns16550a0] enabled
[    0.000000] Initrd not found or empty - disabling initrd
[    0.000000] Using appended Device Tree.
[    0.000000] Primary instruction cache 16kB, VIPT, 4-way, linesize 16 bytes.
[    0.000000] Primary data cache 16kB, 2-way, VIPT, cache aliases, linesize 16 bytes
[    0.000000] Zone ranges:
[    0.000000]   Normal   [mem 0x0000000000000000-0x0000000007ffffff]
[    0.000000] Movable zone start for each node
[    0.000000] Early memory node ranges
[    0.000000]   node   0: [mem 0x0000000000000000-0x0000000007ffffff]
[    0.000000] Initmem setup node 0 [mem 0x0000000000000000-0x0000000007ffffff]
[    0.000000] pcpu-alloc: s0 r0 d32768 u32768 alloc=1*32768
[    0.000000] pcpu-alloc: [0] 0 
[    0.000000] Kernel command line: earlycon
[    0.000000] Dentry cache hash table entries: 16384 (order: 4, 65536 bytes, linear)
[    0.000000] Inode-cache hash table entries: 8192 (order: 3, 32768 bytes, linear)
[    0.000000] Built 1 zonelists, mobility grouping on.  Total pages: 32480
[    0.000000] mem auto-init: stack:off, heap alloc:off, heap free:off
[    0.000000] Memory: 119460K/131072K available (6649K kernel code, 619K rwdata, 1556K rodata, 1216K init, 223K bss, 11612K reserved, 0K cma-reserved)
[    0.000000] SLUB: HWalign=32, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
[    0.000000] NR_IRQS: 256
[    0.000000] rtl83xx-clk: initialized, CPU 500 MHz, MEM 300 MHz (8 Bit DDR3), LXB 200 MHz
[    0.000000] clocksource: realtek_otto_timer: mask: 0xfffffff max_cycles: 0xfffffff, max_idle_ns: 38225208801 ns
[    0.000006] sched_clock: 28 bits at 3125kHz, resolution 320ns, wraps every 42949672800ns
[    0.009465] Calibrating delay loop... 498.89 BogoMIPS (lpj=2494464)
[    0.136034] pid_max: default: 32768 minimum: 301
[    0.157373] Mount-cache hash table entries: 1024 (order: 0, 4096 bytes, linear)
[    0.165651] Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes, linear)
[    0.192938] RCU Tasks Trace: Setting shift to 0 and lim to 1 rcu_task_cb_adjust=1.
[    0.217337] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns
[    0.228411] futex hash table entries: 256 (order: -1, 3072 bytes, linear)
[    0.240360] pinctrl core: initialized pinctrl subsystem
[    0.252512] NET: Registered PF_NETLINK/PF_ROUTE protocol family
[    0.286390] rtl83xx-clk clock-controller: rate setting enabled, CPU 300-625 MHz, MEM 300-300 MHz, LXB 200-200 MHz, OVERCLOCK AT OWN RISK
[    0.306027] clocksource: Switched to clocksource realtek_otto_timer
[    0.332190] NET: Registered PF_INET protocol family
[    0.338438] IP idents hash table entries: 2048 (order: 2, 16384 bytes, linear)
[    0.348427] tcp_listen_portaddr_hash hash table entries: 1024 (order: 0, 4096 bytes, linear)
[    0.358027] Table-perturb hash table entries: 65536 (order: 6, 262144 bytes, linear)
[    0.366796] TCP established hash table entries: 1024 (order: 0, 4096 bytes, linear)
[    0.375382] TCP bind hash table entries: 1024 (order: 1, 8192 bytes, linear)
[    0.383412] TCP: Hash tables configured (established 1024 bind 1024)
[    0.392433] MPTCP token hash table entries: 512 (order: 0, 6144 bytes, linear)
[    0.401189] UDP hash table entries: 256 (order: 0, 4096 bytes, linear)
[    0.408674] UDP-Lite hash table entries: 256 (order: 0, 4096 bytes, linear)
[    0.418010] NET: Registered PF_UNIX/PF_LOCAL protocol family
[    0.439667] workingset: timestamp_bits=14 max_order=15 bucket_order=1
[    0.449678] squashfs: version 4.0 (2009/01/31) Phillip Lougher
[    0.456361] jffs2: version 2.2 (NAND) (SUMMARY) (ZLIB) (LZMA) (RTIME) (CMODE_PRIORITY) (c) 2001-2006 Red Hat, Inc.
[    0.477248] pinctrl-single 1b001000.pinmux: 32 pins, size 4
[    0.485093] pinctrl-single 1b00a000.pinmux: 32 pins, size 4
[    0.494699] Probing RTL8231 GPIOs
[    0.498560] rtl8231_init called, MDIO bus ID: 0
[    0.506484] Serial: 8250/16550 driver, 2 ports, IRQ sharing disabled
[    0.517443] printk: console [ttyS0] disabled
[    0.523704] 18002000.uart: ttyS0 at MMIO 0x18002000 (irq = 31, base_baud = 12500000) is a 16550A
[    0.533706] printk: console [ttyS0] enabled
[    0.543198] printk: bootconsole [early0] disabled
[    0.606437] printk: bootconsole [ns16550a0] disabled
[    0.620571] 18002100.uart: ttyS1 at MMIO 0x18002100 (irq = 30, base_baud = 12500000) is a 16550A
[    0.753388] brd: module loaded
[    0.769074] spi-nor spi0.0: w25q128 (16384 Kbytes)
[    0.774929] 7 fixed-partitions partitions found on MTD device spi0.0
[    0.782267] OF: Bad cell count for /soc/spi@1200/flash@0/partitions
[    0.789481] OF: Bad cell count for /soc/spi@1200/flash@0/partitions
[    0.797121] Creating 7 MTD partitions on "spi0.0":
[    0.802535] 0x000000000000-0x000000040000 : "u-boot"
[    0.817041] 0x000000040000-0x000000050000 : "u-boot-env"
[    0.829485] 0x000000050000-0x000000060000 : "u-boot-env2"
[    0.841887] 0x000000060000-0x000000160000 : "jffs"
[    0.853477] 0x000000160000-0x000000260000 : "jffs2"
[    0.865409] 0x000000260000-0x000000930000 : "firmware"
[    0.878228] 2 uimage-fw partitions found on MTD device firmware
[    0.884898] Creating 2 MTD partitions on "firmware":
[    0.890590] 0x000000000000-0x0000003a0000 : "kernel"
[    0.902288] 0x0000003a0000-0x0000006d0000 : "rootfs"
[    0.913929] mtd: setting mtd7 (rootfs) as root device
[    0.919860] 1 squashfs-split partitions found on MTD device rootfs
[    0.926939] 0x000000610000-0x0000006d0000 : "rootfs_data"
[    0.939192] 0x000000930000-0x000001000000 : "runtime2"
[    0.979356] Probing RTL838X eth device pdev: 818a7c00, dev: 818a7c10
[    1.036548] Found SoC ID: 8382: RTL8382, family 8380
[    1.042329] rtl8380_init_mac
[    1.045631] Using MAC 0000f44d5c6b5345
[    1.177608] Realtek RTL8218B (internal) mdio-bus:08: Detected internal RTL8218B
[    1.185845] Firmware loaded. Size 1184, magic: 83808380
[    5.298096] i2c_dev: i2c /dev entries driver
[    5.313457] NET: Registered PF_INET6 protocol family
[    5.354015] Segment Routing with IPv6
[    5.358516] In-situ OAM (IOAM) with IPv6
[    5.363410] NET: Registered PF_PACKET protocol family
[    5.370094] 8021q: 802.1Q VLAN Support v1.8
[    6.049035] Realtek RTL8218B (internal) rtl838x slave mii-0:08: Detected internal RTL8218B
[    6.058436] Firmware loaded. Size 1184, magic: 83808380
[    9.645172] In rtl83xx_vlan_setup
[    9.649058] UNKNOWN_MC_PMASK: 000000001fffffff
[    9.654063] VLAN profile 0: L2 learning: 1, UNKN L2MC FLD PMSK 511, 		UNKN IPMC FLD PMSK 511, UNKN IPv6MC FLD PMSK: 511
[   10.746063] rtl83xx_enable_phy_polling:           ffffff
[   11.786422] rtl83xx-switch switch@1b000000: configuring for fixed/internal link mode
[   11.795656] rtl83xx-switch switch@1b000000: Link is Up - 1Gbps/Full - flow control off
[   11.815658] rtl83xx-switch switch@1b000000 lan1 (uninitialized): PHY [mdio-bus:00] driver [REALTEK RTL8218D] (irq=POLL)
[   11.846240] rtl83xx-switch switch@1b000000 lan2 (uninitialized): PHY [mdio-bus:01] driver [REALTEK RTL8218D] (irq=POLL)
[   11.876413] rtl83xx-switch switch@1b000000 lan3 (uninitialized): PHY [mdio-bus:02] driver [REALTEK RTL8218D] (irq=POLL)
[   11.906816] rtl83xx-switch switch@1b000000 lan4 (uninitialized): PHY [mdio-bus:03] driver [REALTEK RTL8218D] (irq=POLL)
[   11.937032] rtl83xx-switch switch@1b000000 lan5 (uninitialized): PHY [mdio-bus:04] driver [REALTEK RTL8218D] (irq=POLL)
[   11.967316] rtl83xx-switch switch@1b000000 lan6 (uninitialized): PHY [mdio-bus:05] driver [REALTEK RTL8218D] (irq=POLL)
[   11.997977] rtl83xx-switch switch@1b000000 lan7 (uninitialized): PHY [mdio-bus:06] driver [REALTEK RTL8218D] (irq=POLL)
[   12.028467] rtl83xx-switch switch@1b000000 lan8 (uninitialized): PHY [mdio-bus:07] driver [REALTEK RTL8218D] (irq=POLL)
[   12.048567] rtl83xx-switch switch@1b000000 lan9 (uninitialized): PHY [mdio-bus:08] driver [Realtek RTL8218B (internal)] (irq=POLL)
[   12.080676] rtl83xx-switch switch@1b000000 lan10 (uninitialized): PHY [mdio-bus:09] driver [Realtek RTL8218B (internal)] (irq=POLL)
[   12.112242] rtl83xx-switch switch@1b000000 lan11 (uninitialized): PHY [mdio-bus:0a] driver [Realtek RTL8218B (internal)] (irq=POLL)
[   12.144280] rtl83xx-switch switch@1b000000 lan12 (uninitialized): PHY [mdio-bus:0b] driver [Realtek RTL8218B (internal)] (irq=POLL)
[   12.175710] rtl83xx-switch switch@1b000000 lan13 (uninitialized): PHY [mdio-bus:0c] driver [Realtek RTL8218B (internal)] (irq=POLL)
[   12.207377] rtl83xx-switch switch@1b000000 lan14 (uninitialized): PHY [mdio-bus:0d] driver [Realtek RTL8218B (internal)] (irq=POLL)
[   12.239223] rtl83xx-switch switch@1b000000 lan15 (uninitialized): PHY [mdio-bus:0e] driver [Realtek RTL8218B (internal)] (irq=POLL)
[   12.270804] rtl83xx-switch switch@1b000000 lan16 (uninitialized): PHY [mdio-bus:0f] driver [Realtek RTL8218B (internal)] (irq=POLL)
[   12.302598] rtl83xx-switch switch@1b000000 lan17 (uninitialized): PHY [mdio-bus:10] driver [REALTEK RTL8218D] (irq=POLL)
[   12.333556] rtl83xx-switch switch@1b000000 lan18 (uninitialized): PHY [mdio-bus:11] driver [REALTEK RTL8218D] (irq=POLL)
[   12.364083] rtl83xx-switch switch@1b000000 lan19 (uninitialized): PHY [mdio-bus:12] driver [REALTEK RTL8218D] (irq=POLL)
[   12.394563] rtl83xx-switch switch@1b000000 lan20 (uninitialized): PHY [mdio-bus:13] driver [REALTEK RTL8218D] (irq=POLL)
[   12.425111] rtl83xx-switch switch@1b000000 lan21 (uninitialized): PHY [mdio-bus:14] driver [REALTEK RTL8218D] (irq=POLL)
[   12.455630] rtl83xx-switch switch@1b000000 lan22 (uninitialized): PHY [mdio-bus:15] driver [REALTEK RTL8218D] (irq=POLL)
[   12.486352] rtl83xx-switch switch@1b000000 lan23 (uninitialized): PHY [mdio-bus:16] driver [REALTEK RTL8218D] (irq=POLL)
[   12.516801] rtl83xx-switch switch@1b000000 lan24 (uninitialized): PHY [mdio-bus:17] driver [REALTEK RTL8218D] (irq=POLL)
[   12.532746] rtl838x-eth 1b00a300.ethernet eth0: entered promiscuous mode
[   12.540614] DSA: tree 0 setup
[   12.544105] LINK state irq: 20
[   12.547741] In rtl83xx_setup_qos
[   12.551385] Setting up RTL838X QoS
[   12.555183] RTL838X_PRI_SEL_TBL_CTRL(i): 00033112
[   12.560552] Current Intprio2queue setting: 00000000
[   12.566130] QM_PKT2CPU_INTPRI_MAP: 00fac688
[   12.571177] rtl838x_dbgfs_init called
[   12.577543] rtl83xx_fib_event_work_do: FIB4 default rule failed
[   12.600304] clk: Disabling unused clocks
[   12.617304] VFS: Mounted root (squashfs filesystem) readonly on device 31:7.
[   12.638781] Freeing unused kernel image (initmem) memory: 1216K
[   12.645450] This architecture does not have kernel memory protection.
[   12.652787] Run /sbin/init as init process
[   12.657498]   with arguments:
[   12.660846]     /sbin/init
[   12.663872]   with environment:
[   12.667526]     HOME=/
[   12.670200]     TERM=linux
[   13.583069] init: Console is alive
[   13.588041] init: - watchdog -
[   13.794250] kmodloader: loading kernel modules from /etc/modules-boot.d/*
[   13.802013] RTL8380 Link change: status: 1, ports 10
[   13.819344] gpio_button_hotplug: loading out-of-tree module taints kernel.
[   13.837919] kmodloader: done loading kernel modules from /etc/modules-boot.d/*
[   13.857457] init: - preinit -
[   14.676269] RTL8380 Link change: status: 1, ports 800000
[   14.700363] RTL8380 Link change: status: 1, ports 10
[   17.186205] random: crng init done
[   19.280024] RTL8380 Link change: status: 1, ports 10
[   21.827092] RTL8380 Link change: status: 1, ports 4
[   21.855990] RESETTING 8380, CPU_PORT 28
[   22.061167] rtl838x-eth 1b00a300.ethernet eth0: configuring for fixed/internal link mode
[   22.070229] In rtl838x_mac_config, mode 1
[   22.077802] rtl83xx-switch switch@1b000000 lan1: configuring for phy/qsgmii link mode
[   22.086988] rtl838x-eth 1b00a300.ethernet eth0: Link is Up - 1Gbps/Full - flow control off
[   22.096783] 8021q: adding VLAN 0 to HW filter on device lan1
[   22.103670] rtl83xx_fib_event: FIB_RULE ADD/DEL for IPv6 not supported
[   22.111314] rtl83xx_fib_event: FIB_RULE ADD/DEL for IPv6 not supported
[   22.184773] rtl83xx_fib_event_work_do: FIB4 failed
[   22.192133] rtl83xx_fib_event_work_do: FIB4 failed
[   22.216347] rtl83xx_fib_event_work_do: FIB4 failed
[   23.060037] RTL8380 Link change: status: 1, ports 80
[   23.946164] rtl83xx_fib_event: FIB_RULE ADD/DEL for IPv6 not supported
[   25.958011] RTL8380 Link change: status: 1, ports 4
[   26.277348] RTL8380 Link change: status: 1, ports 10
[   26.818054] jffs2: notice: (809) jffs2_build_xattr_subsystem: complete building xattr subsystem, 13 of xdatum (10 unchecked, 3 orphan) and 16 of xref (3 dead, 0 orphan) found.
[   26.840063] mount_root: switching to jffs2 overlay
[   26.849965] overlayfs: upper fs does not support tmpfile.
[   26.875240] urandom-seed: Seeding with /etc/urandom.seed
[   27.071322] rtl83xx_fib4_del: no such gateway: 0.0.0.0
[   27.077389] rtl83xx_fib4_del: no such gateway: 0.0.0.0
[   27.083205] rtl83xx_fib4_del: no such gateway: 0.0.0.0
[   27.153340] procd: - early -
[   27.157721] procd: - watchdog -
[   28.126363] procd: - watchdog -
[   28.132509] procd: - ubus -
[   28.285234] procd: - init -
[   28.968398] RTL8380 Link change: status: 1, ports 10
[   30.285436] RTL8380 Link change: status: 1, ports 4
[   30.371177] kmodloader: loading kernel modules from /etc/modules.d/*
[   30.405152] kmodloader: done loading kernel modules from /etc/modules.d/*
[   32.416104] RTL8380 Link change: status: 1, ports 4
[   32.456969] urngd: v1.0.2 started.
[   32.976130] RTL8380 Link change: status: 1, ports 4
[   35.492791] RTL8380 Link change: status: 1, ports 40
[   35.851258] rtl83xx_fib_event: FIB_RULE ADD/DEL for IPv6 not supported
[   45.565421] rtl83xx_fib_event: FIB_RULE ADD/DEL for IPv6 not supported
[   45.701608] in rtl838x_eth_stop
[   45.705376] rtl838x-eth 1b00a300.ethernet eth0: Link is Down
[   46.238934] rtl83xx_fib_event: FIB_RULE ADD/DEL for IPv6 not supported
[   46.246489] rtl83xx_fib_event: FIB_RULE ADD/DEL for IPv6 not supported
[   46.253900] rtl83xx_fib_event: FIB_RULE ADD/DEL for IPv6 not supported
[   46.261426] rtl83xx_fib_event: FIB_RULE ADD/DEL for IPv6 not supported
[   46.552788] RESETTING 8380, CPU_PORT 28
[   46.757961] rtl838x-eth 1b00a300.ethernet eth0: configuring for fixed/internal link mode
[   46.767024] In rtl838x_mac_config, mode 1
[   46.777906] rtl838x-eth 1b00a300.ethernet eth0: Link is Up - 1Gbps/Full - flow control off
[   46.788134] rtl83xx_fib_event: FIB_RULE ADD/DEL for IPv6 not supported
[   46.795639] rtl83xx_fib_event: FIB_RULE ADD/DEL for IPv6 not supported
[   47.285792] rtl83xx-switch switch@1b000000 lan1: configuring for phy/qsgmii link mode
[   47.326489] 8021q: adding VLAN 0 to HW filter on device lan1
[   47.577994] switch: port 1(lan1) entered blocking state
[   47.583899] switch: port 1(lan1) entered disabled state
[   47.590001] rtl83xx-switch switch@1b000000 lan1: entered allmulticast mode
[   47.597859] rtl838x-eth 1b00a300.ethernet eth0: entered allmulticast mode
[   47.606695] rtl83xx-switch switch@1b000000 lan1: entered promiscuous mode
[   47.842292] rtl83xx_fib_event: FIB_RULE ADD/DEL for IPv6 not supported
[   47.881572] rtl83xx_fib_event: FIB_RULE ADD/DEL for IPv6 not supported
[   49.119270] rtl83xx_fib_event: FIB_RULE ADD/DEL for IPv6 not supported
[   49.126904] rtl83xx_fib_event: FIB_RULE ADD/DEL for IPv6 not supported
[   49.146403] rtl83xx-switch switch@1b000000 lan2: configuring for phy/qsgmii link mode
[   49.228168] 8021q: adding VLAN 0 to HW filter on device lan2
[   49.520275] switch: port 2(lan2) entered blocking state
[   49.526297] switch: port 2(lan2) entered disabled state
[   49.532263] rtl83xx-switch switch@1b000000 lan2: entered allmulticast mode
[   49.581688] rtl83xx-switch switch@1b000000 lan2: entered promiscuous mode
[   50.276476] rtl83xx-switch switch@1b000000 lan3: configuring for phy/qsgmii link mode
[   50.298365] 8021q: adding VLAN 0 to HW filter on device lan3
[   50.516153] switch: port 3(lan3) entered blocking state
[   50.522062] switch: port 3(lan3) entered disabled state
[   50.528138] rtl83xx-switch switch@1b000000 lan3: entered allmulticast mode
[   50.616758] rtl83xx-switch switch@1b000000 lan3: entered promiscuous mode
[   51.088244] rtl83xx-switch switch@1b000000 lan4: configuring for phy/qsgmii link mode
[   51.103773] 8021q: adding VLAN 0 to HW filter on device lan4
[   51.203148] switch: port 4(lan4) entered blocking state
[   51.209193] switch: port 4(lan4) entered disabled state
[   51.215157] rtl83xx-switch switch@1b000000 lan4: entered allmulticast mode
[   51.246719] rtl83xx-switch switch@1b000000 lan4: entered promiscuous mode
[   51.425609] RTL8380 Link change: status: 1, ports 10
[   51.558790] rtl83xx-switch switch@1b000000 lan5: configuring for phy/qsgmii link mode
[   51.586469] 8021q: adding VLAN 0 to HW filter on device lan5
[   51.698779] switch: port 5(lan5) entered blocking state
[   51.704684] switch: port 5(lan5) entered disabled state
[   51.710768] rtl83xx-switch switch@1b000000 lan5: entered allmulticast mode
[   51.719535] rtl83xx-switch switch@1b000000 lan5: entered promiscuous mode
[   52.066468] rtl83xx-switch switch@1b000000 lan6: configuring for phy/qsgmii link mode
[   52.087007] 8021q: adding VLAN 0 to HW filter on device lan6
[   52.208379] switch: port 6(lan6) entered blocking state
[   52.214288] switch: port 6(lan6) entered disabled state
[   52.220396] rtl83xx-switch switch@1b000000 lan6: entered allmulticast mode
[   52.229262] rtl83xx-switch switch@1b000000 lan6: entered promiscuous mode
[   52.419666] RTL8380 Link change: status: 1, ports 40
[   52.532241] rtl83xx-switch switch@1b000000 lan7: configuring for phy/qsgmii link mode
[   52.548825] 8021q: adding VLAN 0 to HW filter on device lan7
[   52.663622] switch: port 7(lan7) entered blocking state
[   52.669678] switch: port 7(lan7) entered disabled state
[   52.675641] rtl83xx-switch switch@1b000000 lan7: entered allmulticast mode
[   52.684516] rtl83xx-switch switch@1b000000 lan7: entered promiscuous mode
[   52.916942] RTL8380 Link change: status: 1, ports 80
[   53.046466] rtl83xx-switch switch@1b000000 lan8: configuring for phy/qsgmii link mode
[   53.065551] 8021q: adding VLAN 0 to HW filter on device lan8
[   53.166290] switch: port 8(lan8) entered blocking state
[   53.172198] switch: port 8(lan8) entered disabled state
[   53.178296] rtl83xx-switch switch@1b000000 lan8: entered allmulticast mode
[   53.187203] rtl83xx-switch switch@1b000000 lan8: entered promiscuous mode
[   53.514478] rtl83xx-switch switch@1b000000 lan9: configuring for phy/internal link mode
[   53.533977] 8021q: adding VLAN 0 to HW filter on device lan9

ps ww:

root@OpenWrt:~# ps ww
  PID USER       VSZ STAT COMMAND
    1 root      2352 S    /sbin/procd
    2 root         0 SW   [kthreadd]
    3 root         0 SW   [pool_workqueue_]
    4 root         0 IW<  [kworker/R-slub_]
    5 root         0 IW<  [kworker/R-netns]
    7 root         0 IW<  [kworker/0:0H-kb]
    9 root         0 IW<  [kworker/R-mm_pe]
   10 root         0 IW   [rcu_tasks_trace]
   11 root         0 SW   [ksoftirqd/0]
   12 root         0 IW<  [kworker/R-inet_]
   13 root         0 SW   [oom_reaper]
   14 root         0 IW<  [kworker/R-write]
   15 root         0 SW   [kcompactd0]
   16 root         0 IW<  [kworker/R-kbloc]
   17 root         0 SW   [watchdogd]
   18 root         0 SW   [kswapd0]
   27 root         0 IW<  [kworker/R-kthro]
   57 root         0 IW   [kworker/u2:3-ev]
  115 root         0 SW   [spi0]
  332 root         0 IW<  [kworker/R-mld]
  334 root         0 IW<  [kworker/R-ipv6_]
  335 root         0 IW<  [kworker/R-dsa_o]
  567 root         0 IW<  [kworker/0:1H]
  810 root         0 SWN  [jffs2_gcd_mtd8]
  883 ubus      1512 S    /sbin/ubusd
  884 root      1084 S    /sbin/askfirst /usr/libexec/login.sh
  919 root      1236 S    /sbin/urngd
 1097 logd      2284 S    /sbin/logd -S 64
 1151 root      3468 S    /sbin/rpcd -s /var/run/ubus/ubus.sock -t 30
 1274 root      1300 S    /usr/sbin/dropbear -F -P /var/run/dropbear.1.pid -p 23425 -s -g -K 300 -T 3 -W 262144
 1337 root      2740 S    /sbin/netifd
 1530 root      3056 S    /usr/sbin/uhttpd -f -h /www -r OpenWrt -x /cgi-bin -u /ubus -t 60 -T 30 -k 20 -A 1 -n 3 -N 100 -R -p 0.0.0.0:80 -p [::]:80 -C /etc/uhttpd.crt -K /etc/uhttpd.key -s 0.0.0.0:443 -s [::]:443
 1624 root     34956 S    /usr/bin/realtek-poe
 1932 root      1396 S<   /usr/sbin/ntpd -n -N -S /usr/sbin/ntpd-hotplug -p ntp.z51
 2048 root      1320 S    /usr/sbin/dropbear -F -P /var/run/dropbear.1.pid -p 23425 -s -g -K 300 -T 3 -W 262144 -2 9
 2049 root      1404 S    -sh
 2332 root         0 IW   [kworker/u2:0-ev]
 2568 root         0 IW   [kworker/0:0-eve]
 2790 root         0 IW   [kworker/0:6-eve]
 2882 root         0 IW   [kworker/0:4-eve]
 2891 root         0 IW   [kworker/0:10-ev]
 2994 root         0 IW   [kworker/u2:1-ev]
 3005 root         0 IW   [kworker/0:5-eve]
 3013 root         0 IW   [kworker/0:7-eve]
 3023 root         0 IW   [kworker/0:8-eve]
 3031 root         0 IW   [kworker/0:11-ev]
 3032 root         0 IW   [kworker/0:3-eve]
 3040 root         0 IW   [kworker/0:1-eve]
 3041 root         0 IW   [kworker/0:9-eve]
 3049 root         0 IW   [kworker/0:2-eve]
 3056 root      1404 R    ps ww

Thank you!

Oh yeah, there's the problem. You are running two processes talking to the PoE controller at the same time. Bound to go wrong, have to disable one of them.

For example, if poemgr-realtek from this thread is installed to the suggested location, then:

  • service realtek-poe disable
  • service poemgr-realtek enable
  • power cycle the switch

Should do the job.

(You can try rebooting instead of power cycling, but it won't reset the PSE chips, that circuitry stays on during reboots. Because the controller is currently not sending valid checksums, I imagine a power cycle might be better?..)

For testing just once instead of actually switching daemon, my suggestion is: (1) disable both services, (2) power cycle switch with both disabled and then (3) run service poemgr-realtek start after the system has booted to do a single start of that specific daemon. (Notice "start" instead of "enable".) Then check debug output with logread command.

The service definition file listed further above already has the "-d" flag enabled, refer to "procd_set_param command" on line 17. Adding "-v" for verbose makes the output a bit easier to read and "-f" will make the daemon start correctly in cases where the fw_sys or fw_env environment is not working. I would suggest adding both options to line 17 of the /etc/init.d/poemgr-realtek file for this test.

Hope to see good results :crossed_fingers: :slight_smile:

Uh, sorry for not having seen the obvious. I figured the services are unrelated and the pre-installed - well - making PoE work in general, while yours enables fine grained control. Anyway, obviously didn't think too much about it :slight_smile:

Without a power cycle:

root@OpenWrt:~# /opt/poe-mgr -v -d -f
skip board revision check
configured device: /dev/ttyS1, baud: 115200, data bits: 8, parity: none, stop bits: 1, flow control: off
wait for quiescent /dev/ttyS1
request controller status
TX -> 40 01 ff ff ff ff ff ff ff ff ff 38
RX <- e0 62 00 00 00 00 00 00 00 00 00 42
unsolicited frame, add to queue
timeout while waiting for response
ignoring 1 unknown notifications from controller
Retrying...
TX -> 40 02 ff ff ff ff ff ff ff ff ff 39
RX <- 40 02 ff 10 01 01 38 10 01 04 11 b1
controller status request ok, 16 ports
loaded global policy [budget]
loaded per-port policy [port-ids: 12] [port-prios: 12] [port-enables: 12] [port-idnames: 12]
set power limits, global budget: 130, port guard: 11
TX -> 04 03 00 05 14 00 6e ff ff ff ff 8a
RX <- 04 03 00 00 ff ff ff ff ff ff ff 00
set priority 2 for port 0
TX -> 15 04 00 02 ff ff ff ff ff ff ff 14
RX <- 15 04 00 00 ff ff ff ff ff ff ff 12
set priority 2 for port 1
TX -> 15 05 01 02 ff ff ff ff ff ff ff 16
RX <- 15 05 01 00 ff ff ff ff ff ff ff 14
set priority 2 for port 2
TX -> 15 06 02 02 ff ff ff ff ff ff ff 18
RX <- 15 06 02 00 ff ff ff ff ff ff ff 16
set priority 2 for port 3
TX -> 15 07 03 02 ff ff ff ff ff ff ff 1a
RX <- 15 07 03 00 ff ff ff ff ff ff ff 18
set priority 2 for port 4
TX -> 15 08 04 02 ff ff ff ff ff ff ff 1c
RX <- 15 08 04 00 ff ff ff ff ff ff ff 1a
set priority 2 for port 5
TX -> 15 09 05 02 ff ff ff ff ff ff ff 1e
RX <- 15 09 05 00 ff ff ff ff ff ff ff 1c
set priority 2 for port 6
TX -> 15 0a 06 02 ff ff ff ff ff ff ff 20
RX <- 15 0a 06 00 ff ff ff ff ff ff ff 1e
set priority 2 for port 7
TX -> 15 0b 07 02 ff ff ff ff ff ff ff 22
RX <- 15 0b 07 00 ff ff ff ff ff ff ff 20
set priority 2 for port 8
TX -> 15 0c 08 02 ff ff ff ff ff ff ff 24
RX <- 15 0c 08 00 ff ff ff ff ff ff ff 22
set priority 2 for port 9
TX -> 15 0d 09 02 ff ff ff ff ff ff ff 26
RX <- 15 0d 09 00 ff ff ff ff ff ff ff 24
set priority 2 for port 10
TX -> 15 0e 0a 02 ff ff ff ff ff ff ff 28
RX <- 15 0e 0a 00 ff ff ff ff ff ff ff 26
set priority 2 for port 11
TX -> 15 0f 0b 02 ff ff ff ff ff ff ff 2a
RX <- 15 0f 0b 00 ff ff ff ff ff ff ff 28
enable power on port 0
TX -> 01 10 00 01 ff ff ff ff ff ff ff 0b
RX <- 01 10 00 00 ff ff ff ff ff ff ff 0a
enable power on port 1
TX -> 01 11 01 01 ff ff ff ff ff ff ff 0d
RX <- 01 11 01 00 ff ff ff ff ff ff ff 0c
enable power on port 2
TX -> 01 12 02 01 ff ff ff ff ff ff ff 0f
RX <- 01 12 02 00 ff ff ff ff ff ff ff 0e
enable power on port 3
TX -> 01 13 03 01 ff ff ff ff ff ff ff 11
RX <- 01 13 03 00 ff ff ff ff ff ff ff 10
enable power on port 4
TX -> 01 14 04 01 ff ff ff ff ff ff ff 13
RX <- 01 14 04 00 ff ff ff ff ff ff ff 12
enable power on port 5
TX -> 01 15 05 01 ff ff ff ff ff ff ff 15
RX <- 01 15 05 00 ff ff ff ff ff ff ff 14
enable power on port 6
TX -> 01 16 06 01 ff ff ff ff ff ff ff 17
RX <- 01 16 06 00 ff ff ff ff ff ff ff 16
enable power on port 7
TX -> 01 17 07 01 ff ff ff ff ff ff ff 19
RX <- 01 17 07 00 ff ff ff ff ff ff ff 18
enable power on port 8
TX -> 01 18 08 01 ff ff ff ff ff ff ff 1b
RX <- 01 18 08 00 ff ff ff ff ff ff ff 1a
enable power on port 9
TX -> 01 19 09 01 ff ff ff ff ff ff ff 1d
RX <- 01 19 09 00 ff ff ff ff ff ff ff 1c
enable power on port 10
TX -> 01 1a 0a 01 ff ff ff ff ff ff ff 1f
RX <- 01 1a 0a 00 ff ff ff ff ff ff ff 1e
enable power on port 11
TX -> 01 1b 0b 01 ff ff ff ff ff ff ff 21
RX <- 01 1b 0b 00 ff ff ff ff ff ff ff 20