From: John Crispin Date: Tue, 24 Nov 2020 16:53:47 +0000 (+0100) Subject: rtl83xx-poe: add package X-Git-Url: http://git.openwrt.org/openwrt/feeds.git?a=commitdiff_plain;h=2540faec92abf8f5e52eae0e77bfbdb47457252d;p=openwrt%2Fstaging%2Fblogic.git rtl83xx-poe: add package Signed-off-by: John Crispin --- 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 +}