[PATCH v2] rtl83xx-poe: add package

Stijn Segers foss at volatilesystems.org
Sat Mar 13 17:25:12 GMT 2021



"Bjørn Mork" <bjorn at mork.no> schreef op 13 maart 2021 17:54:19 CET:
>From: John Crispin <john at phrozen.org>
>
>This package implements the microcontroller protocol used to
>talk Broadcom PSE controllers on a number of realtek switches.
>It is required to enable PoE ouput on supported hardware.
>
>The implemented ABI allows individial control and monitoring
>of each PoE port using ubus.  Example from a ZyXEL GS1900-10HP:
>
>root at gs1900-10hp:~# ubus -v list poe
>'poe' @3c3a28fb
>        "info":{}
>        "port":{"enable":"Boolean","port":"Integer"}
>root at gs1900-10hp:~# ubus call poe info
>{
>        "ports": [
>                "enabled",
>                "enabled",
>                "0W",
>                "enabled",
>                "enabled",
>                "enabled",
>                "4.6W",
>                "4W"
>        ],
>        "power_budget": "77W",
>        "power_consumption": "7.8W"
>}
>
>Tested-by: Birger Koblitz <mail at birger-koblitz.de>
>Signed-off-by: John Crispin <john at phrozen.org>
>Signed-off-by: Bjørn Mork <bjorn at mork.no> [commit message, release number]

Tested-by: Stijn Segers <foss at volatilesystems.org>
>---
>"Adrian Schmutzler" <mail at adrianschmutzler.de> writes:
>
>> Is this needed in core repo?
>
>I believe it is.  This package (or another implementation of the protocol) is
>required to turn on the PoE hardware on a number of realtek switches.  I must
>admit that I'm not completely sure about the policies wrt core vs packages,
>but my understanding is that hardware enabling packages belong in core.
>
>Will follow-up with a patch adding this to DEVICE_PACKAGES of the affected
>hardware, replacing the current lua-rs232 dependency (which is really this
>package).
>
>
>Bjørn
>
> package/rtl83xx-poe/Makefile             |  29 +++
> package/rtl83xx-poe/files/bin/poe.lua    | 316 +++++++++++++++++++++++
> package/rtl83xx-poe/files/etc/config/poe |  10 +
> package/rtl83xx-poe/files/etc/init.d/poe |  18 ++
> 4 files changed, 373 insertions(+)
> create mode 100644 package/rtl83xx-poe/Makefile
> create mode 100755 package/rtl83xx-poe/files/bin/poe.lua
> create mode 100644 package/rtl83xx-poe/files/etc/config/poe
> create mode 100755 package/rtl83xx-poe/files/etc/init.d/poe
>
>diff --git a/package/rtl83xx-poe/Makefile b/package/rtl83xx-poe/Makefile
>new file mode 100644
>index 000000000000..226e6ce694c4
>--- /dev/null
>+++ b/package/rtl83xx-poe/Makefile
>@@ -0,0 +1,29 @@
>+include $(TOPDIR)/rules.mk
>+
>+PKG_NAME:=rtl83xx-poe
>+PKG_RELEASE:=1
>+
>+PKG_LICENSE:=GPL-2.0-or-later
>+
>+include $(INCLUDE_DIR)/package.mk
>+
>+define Package/rtl83xx-poe
>+  SECTION:=utils
>+  CATEGORY:=Utilities
>+  DEPENDS:=+libubox-lua +libubus-lua +libuci-lua +lua-rs232
>+  TITLE:=PoE daemon for realtek switches
>+endef
>+
>+define Package/rtl83xx-poe/description
>+ This package contains an utility to allow triggering the PoE state of realtek switch ports.
>+endef
>+
>+define Build/Compile
>+
>+endef
>+
>+define Package/rtl83xx-poe/install
>+	$(CP) ./files/* $(1)/
>+endef
>+
>+$(eval $(call BuildPackage,rtl83xx-poe))
>diff --git a/package/rtl83xx-poe/files/bin/poe.lua b/package/rtl83xx-poe/files/bin/poe.lua
>new file mode 100755
>index 000000000000..86dafe13cd01
>--- /dev/null
>+++ b/package/rtl83xx-poe/files/bin/poe.lua
>@@ -0,0 +1,316 @@
>+#!/usr/bin/lua
>+local rs = require "luars232"
>+
>+port_name = "/dev/ttyS1"
>+out = io.stderr
>+nseq = 0
>+
>+budget = 65.0
>+port_power = {0, 0, 0, 0, 0, 0, 0, 0 }
>+
>+if arg[1] ~= nil then
>+	budget = tonumber(arg[1])
>+end
>+for i = 1, 8 do
>+	port_power[i] = arg[i + 1]
>+end
>+
>+function initSerial(p)
>+	local e, p = rs.open(p)
>+	if e ~= rs.RS232_ERR_NOERROR then
>+		-- handle error
>+		out:write(string.format("can't open serial port '%s', error: '%s'\n",
>+				port_name, rs.error_tostring(e)))
>+		return
>+	end
>+
>+	assert(p:set_baud_rate(rs.RS232_BAUD_19200) == rs.RS232_ERR_NOERROR)
>+	assert(p:set_data_bits(rs.RS232_DATA_8) == rs.RS232_ERR_NOERROR)
>+	assert(p:set_parity(rs.RS232_PARITY_NONE) == rs.RS232_ERR_NOERROR)
>+	assert(p:set_stop_bits(rs.RS232_STOP_1) == rs.RS232_ERR_NOERROR)
>+	assert(p:set_flow_control(rs.RS232_FLOW_OFF)  == rs.RS232_ERR_NOERROR)
>+
>+	out:write(string.format("OK, port open with values '%s'\n", tostring(p)))
>+
>+	return p
>+end
>+
>+function receive(pCon)
>+	local reply = {}
>+	local retries = 0
>+
>+	while table.getn(reply) < 12 and retries < 4 do
>+		-- Read up to 12 byte response, timeout 400ms
>+		err, data_read, size = pCon:read(12, 400)
>+		assert(err == rs.RS232_ERR_NOERROR)
>+--		io.write(string.format("-> [%2d]:", string.len(data_read)))
>+		for i = 1, string.len(data_read) do
>+			table.insert(reply, string.byte(string.sub(data_read, i, i)))
>+--			io.write(string.format(" %02x", reply[i]))
>+		end
>+--		io.write("\n")
>+		retries = retries + 1
>+	end
>+	if table.getn(reply) ~= 12 then
>+		print ("Unexpected length!")
>+		return(nil)
>+	end
>+	local sum = 0
>+	for i = 1, 11 do
>+		sum = sum + reply[i]
>+	end
>+	if sum % 256 ~= reply[12] then
>+		print ("Checksum error!")
>+		return(nil)
>+	end
>+	return(reply)
>+end
>+
>+function sendCommand(pCon, cmd)
>+	nseq = nseq + 1
>+	cmd[2] = nseq % 256
>+
>+	while table.getn(cmd) < 11 do
>+		table.insert(cmd, 0xff)
>+	end
>+	local c_string = ""
>+	local sum = 0
>+--	io.write("send  ")
>+	for i = 1, 11 do
>+		sum = sum + cmd[i]
>+--		io.write(string.format(" %02x", cmd[i]))
>+		c_string = c_string .. string.char(cmd[i])
>+	end
>+--	io.write(string.format(" %02x\n", sum % 256))
>+	c_string = c_string .. string.char(sum % 256)
>+	err, len_written = pCon:write(c_string)
>+	assert(err == rs.RS232_ERR_NOERROR)
>+
>+	local reply = receive(pCon)
>+	if reply then
>+--		io.write("recv  ")
>+--		dumpReply(reply)
>+		if (reply[1] == cmd[1] and reply[2] == cmd[2]) then
>+			return(reply)
>+		else
>+			if reply[1] == 0xfd then
>+				print ("An incomplete request was received!")
>+			elseif reply[1] == 0xfe then
>+				print ("Request frame checksum was incorrect!")
>+			elseif reply[1] == 0xff then
>+				print ("Controller was not ready to respond !")
>+			else
>+				print ("Sequence number mismatch!")
>+			end
>+		end
>+	else
>+		print ("Missing reply!")
>+	end
>+	return(nil)
>+end
>+
>+function dumpReply(reply)
>+	for i,v in ipairs(reply) do
>+		io.write(string.format(" %02x", v))
>+	end
>+	io.write("\n");
>+end
>+
>+function getStatus(pCon)
>+	local cmd = {0x20, 0x01}
>+	local reply = sendCommand(pCon, cmd)
>+	if not reply then return(nil) end
>+	-- returns status, PoEExtVersion, PoEVersion, state2
>+	return({reply[5], reply[6], reply[7], reply[10]})
>+end
>+
>+function disablePort(pCon, port)
>+	local cmd = {0x00, port, port, 0x00}
>+	-- disable command is always sent twice
>+	sendCommand(pCon, cmd)
>+	sendCommand(pCon, cmd)
>+end
>+
>+function enablePort(pCon, port)
>+	local cmd = {0x00, port, port, 0x01}
>+	sendCommand(pCon, cmd)
>+end
>+
>+function setPortRelPrio(pCon, port, prio)
>+	local cmd = {0x1d, 0x00, port, prio}
>+	sendCommand(pCon, cmd)
>+end
>+
>+function setGlobalPowerBudget(pCon, maxPower, guard)
>+	-- maxPower and guard Watts
>+	local cmd = {0x18, 0x01, 0x00}
>+	table.insert(cmd, math.floor(maxPower * 10 / 256))
>+	table.insert(cmd, math.floor(maxPower * 10) % 256)
>+	table.insert(cmd, math.floor(guard * 10 / 256))
>+	table.insert(cmd, math.floor(guard * 10) % 256)
>+	sendCommand(pCon, cmd)
>+end
>+
>+function setPowerLowAction(pCon, disableNext)
>+	local cmd = {0x17, 0x00}
>+	if disableNext then
>+		table.insert(cmd, 0x04)
>+	else
>+		table.insert(cmd, 0x02)
>+	end
>+	sendCommand(pCon, cmd)
>+end
>+
>+function getPowerStat(pCon)
>+	local cmd = {0x23, 0x01}
>+	local reply = sendCommand(pCon, cmd)
>+	if not reply then return(nil) end
>+	local watts = (reply[3] * 256 + reply[4]) / 10.0
>+	return watts
>+end
>+
>+function getPortPower(pCon, port)
>+	local cmd = {0x30, 0x01, port}
>+	local reply = sendCommand(pCon, cmd)
>+	if not reply then return(nil) end
>+	local watts = (reply[10] * 256 + reply[11]) / 10.0
>+	local mamps = reply[6] * 256 + reply[7]
>+	return({watts, mamps})
>+end
>+
>+function getPortOverview(pCon)
>+	local cmd = {0x2a, 0x01, 0x00}
>+	local reply = sendCommand(pCon, cmd)
>+	if not reply then return(nil) end
>+	local s = { }
>+	for i = 4, 11 do
>+		if reply[i] == 0x10 then
>+			s[i-3] = "off"
>+		elseif reply[i] == 0x11 then
>+			s[i-3] = "enabled"
>+		elseif reply[i] > 0x11 then
>+			s[i-3] = "active"
>+		else
>+			s[i-3] = "unknown"
>+		end
>+	end
>+	return(s)
>+end
>+
>+-- Priority for power: 3: High, 2: Normal, 1: Low?
>+function setPortPriority(pCon, port, prio)
>+	local cmd = {0x1a, port, port, prio}
>+	local reply = sendCommand(pCon, cmd)
>+	if not reply then return(nil) end
>+	return(unpack(reply, 4, 11))
>+end
>+
>+function getPortPowerLimits(pCon, port)
>+	local cmd = {0x26, 0x01, port}
>+	local reply = sendCommand(pCon, cmd)
>+	if not reply then return(nil) end
>+	return(reply)
>+end
>+
>+function startupPoE(pCon)
>+	local reply = nil
>+	reply = getStatus(pCon)
>+
>+	setGlobalPowerBudget(pCon, 0, 0)
>+	setPowerLowAction(pCon, nil)
>+	-- do something unknown
>+	sendCommand(pCon, {0x06, 0x00, 0x01})
>+	for i = 0, 7 do
>+		if port_power[i + 1] ~= "1" then
>+			disablePort(pCon, i)
>+		end
>+	end
>+	-- do something unknown
>+	sendCommand(pCon, {0x02, 0x00, 0x01})
>+
>+	for i = 0, 7 do
>+		if port_power[i + 1] ~= "1" then
>+			disablePort(pCon, i)
>+		end
>+	end
>+	-- do something unknown
>+	sendCommand(pCon, {0x02, 0x00, 0x01})
>+
>+	-- use monitor command 25
>+	sendCommand(pCon, {0x25, 0x01})
>+
>+	setGlobalPowerBudget(pCon, 65.0, 7.0)
>+	getPowerStat(pCon)
>+	-- -> 23 01 00 00 02 44 00 02 ff ff 00 6a
>+
>+	-- Set 4 unknown port properties:
>+	for i = 0, 7 do
>+		sendCommand(pCon, {0x11, i, i, 0x01})
>+		sendCommand(pCon, {0x13, i, i, 0x02})
>+		sendCommand(pCon, {0x15, i, i, 0x01})
>+		sendCommand(pCon, {0x10, i, i, 0x03})
>+	end
>+	for i = 0, 7 do
>+		if port_power[i + 1] == "1" then
>+			enablePort(pCon, i)
>+		end
>+	end
>+
>+end
>+
>+local p = initSerial(port_name)
>+startupPoE(p)
>+
>+require "ubus"
>+require "uloop"
>+
>+uloop.init()
>+
>+local conn = ubus.connect()
>+if not conn then
>+        error("Failed to connect to ubus")
>+end
>+
>+local my_method = {
>+	poe = {
>+		info = {
>+			function(req, msg)
>+				local reply = {}
>+
>+				reply.power_consumption = tostring(getPowerStat(p)).."W"
>+				reply.power_budget = tostring(budget).."W"
>+
>+				reply.ports = {}
>+				local s = getPortOverview(p)
>+				for i = 1, 8 do
>+					if s[i] == "active" then
>+						local r = getPortPower(p, i - 1)
>+						reply.ports[i] = tostring(r[1]).."W"
>+					else
>+						reply.ports[i] = s[i]
>+					end
>+				end
>+				conn:reply(req, reply);
>+			end, {}
>+		},
>+		port = {
>+			function(req, msg)
>+				local reply = {}
>+				if msg.port < 1 or msg.port > 8 then
>+					conn:reply(req, false);
>+					return -1
>+				end
>+				if msg.enable == true then
>+					enablePort(p, msg.port - 1)
>+				else
>+					disablePort(p, msg.port - 1)
>+				end
>+				conn:reply(req, reply);
>+			end, {port = ubus.INT32, enable = ubus.BOOLEAN }
>+		},
>+	},
>+}
>+
>+conn:add(my_method)
>+
>+uloop.run()
>diff --git a/package/rtl83xx-poe/files/etc/config/poe b/package/rtl83xx-poe/files/etc/config/poe
>new file mode 100644
>index 000000000000..4fc9723c88c7
>--- /dev/null
>+++ b/package/rtl83xx-poe/files/etc/config/poe
>@@ -0,0 +1,10 @@
>+config poe poe
>+	option budget	65
>+	option port1	0
>+	option port2	0
>+	option port3	0
>+	option port4	0
>+	option port5	0
>+	option port6	0
>+	option port7	0
>+	option port8	0
>diff --git a/package/rtl83xx-poe/files/etc/init.d/poe b/package/rtl83xx-poe/files/etc/init.d/poe
>new file mode 100755
>index 000000000000..159340b03a38
>--- /dev/null
>+++ b/package/rtl83xx-poe/files/etc/init.d/poe
>@@ -0,0 +1,18 @@
>+#!/bin/sh /etc/rc.common
>+START=40
>+
>+USE_PROCD=1
>+PROG=/bin/poe.lua
>+
>+start_service() {
>+	local budget=$(uci get poe.poe.budget)
>+
>+	procd_open_instance
>+	procd_set_param command "$PROG"
>+	procd_append_param command ${budget:-65}
>+	for p in `seq 1 8`; do
>+		local pwr=$(uci get poe.poe.port$p)
>+		procd_append_param command  ${pwr:-0}
>+	done
>+	procd_close_instance
>+}
>-- 
>2.20.1
>
>
>_______________________________________________
>openwrt-devel mailing list
>openwrt-devel at lists.openwrt.org
>https://lists.openwrt.org/mailman/listinfo/openwrt-devel

-- 
Verstuurd vanaf mijn Android apparaat met K-9 Mail. Excuseer mijn beknoptheid.



More information about the openwrt-devel mailing list