Message ID | 20210309211800.25877-1-bjorn@mork.no |
---|---|
State | Changes Requested |
Headers | show |
Series | rtl83xx-poe: add package | expand |
Run tested by me on a DGS1210-10P. Please merge, Birger On 09/03/2021 22:18, Bjørn Mork wrote: > From: John Crispin <john@phrozen.org> > > Signed-off-by: John Crispin <john@phrozen.org> > Signed-off-by: Bjørn Mork <bjorn@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 > +}
Hi, > -----Original Message----- > From: openwrt-devel [mailto:openwrt-devel-bounces@lists.openwrt.org] > On Behalf Of Bjørn Mork > Sent: Dienstag, 9. März 2021 22:18 > To: openwrt-devel@lists.openwrt.org > Cc: John Crispin <john@phrozen.org>; Bjørn Mork <bjorn@mork.no> > Subject: [PATCH] rtl83xx-poe: add package > > From: John Crispin <john@phrozen.org> > > Signed-off-by: John Crispin <john@phrozen.org> > Signed-off-by: Bjørn Mork <bjorn@mork.no> This lacks a commit message. Is this needed in core repo? A few nitpicks below. > --- > 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 Should be "1" or $(COMMITCOUNT) > + > +PKG_LICENSE:=GPL-2.0+ GPL-2.0-or-later Best Adrian > + > +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
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 +}