[PATCH] rtl83xx-poe: add package

Bjørn Mork bjorn at mork.no
Tue Mar 9 21:18:00 GMT 2021


From: John Crispin <john at phrozen.org>

Signed-off-by: John Crispin <john at phrozen.org>
Signed-off-by: Bjørn Mork <bjorn at mork.no>
---
This is John's simple PoE daemon for the realtek devices, which has been
cycling around in assorted repos since last year.  It's well tested by
now.  I've been running this with constant polling from cacti for a
few months now without any issues.

I believe this should be included in master to make it easier to find
and install for new users.



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..195b1eb20949
--- /dev/null
+++ b/package/rtl83xx-poe/Makefile
@@ -0,0 +1,29 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=rtl83xx-poe
+PKG_RELEASE:=2
+
+PKG_LICENSE:=GPL-2.0+
+
+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




More information about the openwrt-devel mailing list