"Bjørn Mork" <bj...@mork.no> schreef op 13 maart 2021 17:54:19 CET: >From: John Crispin <j...@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@gs1900-10hp:~# ubus -v list poe >'poe' @3c3a28fb > "info":{} > "port":{"enable":"Boolean","port":"Integer"} >root@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 <m...@birger-koblitz.de> >Signed-off-by: John Crispin <j...@phrozen.org> >Signed-off-by: Bjørn Mork <bj...@mork.no> [commit message, release number]
Tested-by: Stijn Segers <f...@volatilesystems.org> >--- >"Adrian Schmutzler" <m...@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@lists.openwrt.org >https://lists.openwrt.org/mailman/listinfo/openwrt-devel -- Verstuurd vanaf mijn Android apparaat met K-9 Mail. Excuseer mijn beknoptheid. _______________________________________________ openwrt-devel mailing list openwrt-devel@lists.openwrt.org https://lists.openwrt.org/mailman/listinfo/openwrt-devel