Support for RTL838x based managed switches

Looks like the device tree for GS1900-8HP needs updating with status = "disabled"; at GPIO1.

Can you try that out on your switch? Seems to be limited to the RTL8380 on the 8 & 10 port Zyxel models cos I have the 48 port Zyxel running without issues here.

1 Like

I will try to do that, changing config files for the device tree I would have to build / compile the whole image from the sources, right ?

Does this have no effect on the functionality of the switch?

I mean, the associated issue with this information is not yet in “resolved” status.

Svanheule also commented on this about 2 years ago as a “test”, see above. It seemed more like a workaround to get the image bootable “quick and dirty” in the first place.

IIRC the HP 1920 OEM firmware also has cable testing.

As it happens there is cable test support in OpenWrt via the infrastructure in the Linux kernel (v5.8 or so and later) for cable testing, and on the user-space side in ethtool (ethtool-full package). It's only implemented for Marvell PHYs at the moment, but it should be possible to add support for Realtek PHYs too. I'm currently working on a fix for the Marvell PHY driver on my hardware (Huawei AP5030DN access point). If anyone has any other Marvell PHY devices please chime in on: Ethernet cable test with raw TDR data reporting bug fix - testers needed which includes more details on the feature - and/or the associated github issue (which includes a debug patch which fixes TDR on the AP5030DN).

3 Likes

This should already have kernel support I think including full TDR (mainline kernel drivers/net/phy/marvell.c declares both cable_test_tdr_start and cable_test_tdr_start functions for the 88E1545.

Try installing ethtool-full and running:

ethtool --cable-test devname

and:

ethtool --cable-test-tdr devname

See link in previous post (I have TDR working here on OpenWrt on a 88E1512 in a cheap Huawei access point) - also the link which has more detail and a timeout related patch.

1 Like

Issue looks like it stalled due to lack of interest/feedback.

Let's see if disabling the GPIO works, if we can narrow it down to the GPIO then maybe someone else will have a neater or more sophisticated path but it's a step forward.

You'll need to build your own image, start with the main branch for a snapshot build and add that one line then make the image for your device.

Awesome thanks!

Yeah I didn't get to looking for the code in the mainline kernel.
Trying to find public datasheets was so I could find other implementations for reference.
That's the final piece of the puzzle I guess.
Thanks for pointing out that!
I'll have a read if I want to investigate this further myself =)

Yeah In some later posts I did some reading of the realtek source code.
Link the post below.

I loaded up the 24.10-SNAPSHOT build on a couple of switches via auc upgrade from 23.05.5 and below. No issues on my Netgear GS308T and HPE JG921A (1920-8G-PoE 65W).
Basic use case with management on the native VLAN, will test more typical production installation soon.

This is a recent test, after the opkg->apk changes were reverted back to opkg.

2 Likes

Could you try this instead?


Save to /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()

Save to /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" -d -v -f
	procd_set_param respawn
	procd_set_param stdout 1
	procd_set_param stderr 1
	procd_close_instance
}

Start it up:

root@OpenWrt:~# service realtek-poe disable
root@OpenWrt:~# service poemgr-realtek enable
root@OpenWrt:~# reboot

Let us know if it works?

1 Like

BTW, I manage a few 1920 PoE 370w JG926a for a client. These currently run the stock firmware. When there is a fan failure, the other fans ramp up to full speed and all PoE power output is disabled.

1 Like

That actually sounds like a decent strategy. If a fan fails or the temperature goes too high, kill the PoE to prevent the power electronics from cooking.

I had a couple (3x) of those once. I remember the stock firmware as being quite finicky... If you configured the stacking LAG in a way that it didn't appreciate, it would cut traffic to all ports and start blinking every LED on the box like there was no tomorrow. Quite a show

I just verified the same behavior on my JG925a - one failed (or missing) fan results in PoE being disabled.

For S&G, I tested the script on my Zyxel GS1900-10HP-v2 and it runs well, but the standard 23.05.5 firmware needs package libubox-lua package in addition to the listed dependencies.

Great job! But I'm not convinced yet another device specifc poe manager is the way to go. It doesn't look like the protocol is all that different to the one supported by https://github.com/Hurricos/realtek-poe . How hard would it be to integrate support for the new models into that tool?

(not that it is THE future either, but at least we would avoid even more fragmentation)

Likely true!

I think there's even some documentation of one of the protocols on svanheule.net but I haven't checked it out since the site happened to be down back when I made this.

(... Not sure if there's docs for the broadcom dialect that realtek-poe speaks or the realtek dialect that poemgr-realtek speaks or both. I'll have to check it out soon.)

No idea

Thank you so much for testing and reporting back! :slight_smile:

Great find, I will go fix that in the requirements list right away.

What about this:

1 Like

I am looking to eliminate a Cisco 48port gigabit POE switch and found a FS S3400-48T4SP for a reasonable price. It looks like the older version of the FS S3400-48T6SP. I'm curious if anybody would know if it supports Openwrt? and how hard it would be to get it on it?
I can't link to much, but a quick search on the fs.com site show this and it has the firmware if anybody can help.
https://www.fs.com/products_support/search.html?keyword=90132&page=1

According to a review: Nuvoton M0516LDN high-precision internal oscillator, as well as three Realtek RTL8238B transceivers (not sure if the 8238B would be supported)

According to this it has got Marvell PHYs and a Microchip Main CPU. Which means that it is currently not supported by OpenWrt and it will most probably never be supported unless someone (you?) invests a significant amount of development work.

1 Like

Some of the FS switches are on realtek from what I've seen. S2800-24T4F on RTL8382? Sometimes their datasheet has the switch chip.

But as with anything don't rely on future support. I'd say used JG928A, been having a good time with it.

had a look at the vsc7448 and at least it has reasonable datasheet and register datasheet available. (The registers are attached in the PDF of the main datasheet rather that on the website directly?)

haven't been able to find gpl source for VSC6816 for example. They have a datasheet for software options and they mention linux and u-boot in their documentation. But all I see is a contact sales haha.

JG928Ab looks perfect. Price is good, just 1 fault, the sfp+ ports only support 1000gb. My router is dual sfp+ 10gb and 4x 2.5gbe. I use 2 of the 2.5gbe for gaming PCs, I use 1 of the sfp+ to go to my server and 1 to the switch. This means that I can give my server full 10gbe to my network. Is there a similar hpe one with 10gbe?