From patchwork Sat Mar 13 16:54:19 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Bj=C3=B8rn_Mork?= X-Patchwork-Id: 1452510 X-Patchwork-Delegate: ynezz@true.cz Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=lists.openwrt.org (client-ip=2001:8b0:10b:1:d65d:64ff:fe57:4e05; helo=desiato.infradead.org; envelope-from=openwrt-devel-bounces+incoming=patchwork.ozlabs.org@lists.openwrt.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; secure) header.d=lists.infradead.org header.i=@lists.infradead.org header.a=rsa-sha256 header.s=desiato.20200630 header.b=LciFrj12; dkim=fail reason="signature verification failed" (1024-bit key; secure) header.d=mork.no header.i=@mork.no header.a=rsa-sha256 header.s=b header.b=nsd9Pg+X; dkim-atps=neutral Received: from desiato.infradead.org (desiato.infradead.org [IPv6:2001:8b0:10b:1:d65d:64ff:fe57:4e05]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4DyTN14wN2z9sRR for ; Sun, 14 Mar 2021 03:56:01 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=desiato.20200630; h=Sender:Content-Transfer-Encoding :Content-Type:List-Subscribe:List-Help:List-Post:List-Archive: List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To:Message-Id:Date: Subject:Cc:To:From:Reply-To:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=6Jo3RqL5Yh8BL2++Q/UoHe5xwLzTqJZCZi3sxVDkxiM=; b=LciFrj12PBQVepsSQ3POqmuy+ ZXw20O572x1Wj77rRDzCD8r4xftUgPI+5g5MAXc6C96POlUcf2YeKAlvkI+sWBer+t2eXEopbtkfG ctTn+Ejt6OaY44S1HLRa3gKn1er8A2A4hUuAJsVGXKwMp0cAyIz41P/LlchHvD5uHU1IrKiMC9ft6 cFTd/LpOy7AdofBAv8+R/MOzgPlyAl+lmATE3Go62cK9vzBIAtrnFo0rGUWrWGZSlVHH6o+4TtDCU WM52H2hVt2JmkKoCszIpmJZWFIY3tD2O4JoUDyRKqixtlAmUn+hgKDbDwnMschKAMx8LTyCYiO95e 6/M+ki0yw==; Received: from localhost ([::1] helo=desiato.infradead.org) by desiato.infradead.org with esmtp (Exim 4.94 #2 (Red Hat Linux)) id 1lL7X9-00DOKx-F7; Sat, 13 Mar 2021 16:54:35 +0000 Received: from canardo.mork.no ([2001:4641::1]) by desiato.infradead.org with esmtps (Exim 4.94 #2 (Red Hat Linux)) id 1lL7X3-00DOKT-QK for openwrt-devel@lists.openwrt.org; Sat, 13 Mar 2021 16:54:32 +0000 Received: from canardo.mork.no (ip6-localhost [IPv6:0:0:0:0:0:0:0:1]) by canardo.mork.no (8.15.2/8.15.2) with ESMTPS id 12DGsOtt010760 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=OK); Sat, 13 Mar 2021 17:54:25 +0100 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mork.no; s=b; t=1615654465; bh=AVtGfybMAGKWlsw5MqMn6Cf/OKel2Hp7fo1YR9N6plY=; h=From:To:Cc:Subject:Date:Message-Id:References:From; b=nsd9Pg+XwH5p2tD9aCJsZxT/MI2qzeljOozivwpUAo4wol0QaHLCWV9NcCg8hFM9y TTpTkHr35NSP4JHF7BfTQHiijSWdKTh/l94BKAptOLsdSZwYBjqHoRoy25BwjhZTvS 34PbZi9UbomJtYAsIFTzW87+EHWM2yPnFoUoy85E= Received: (from bjorn@localhost) by canardo.mork.no (8.15.2/8.15.2/Submit) id 12DGsNmA010759; Sat, 13 Mar 2021 17:54:23 +0100 From: =?utf-8?q?Bj=C3=B8rn_Mork?= To: openwrt-devel@lists.openwrt.org Cc: John Crispin , Birger Koblitz , =?utf-8?q?Bj=C3=B8rn_Mork?= Subject: [PATCH v2] rtl83xx-poe: add package Date: Sat, 13 Mar 2021 17:54:19 +0100 Message-Id: <20210313165419.10713-1-bjorn@mork.no> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20210309211244.22684-1-bjorn@mork.no> References: <20210309211244.22684-1-bjorn@mork.no> MIME-Version: 1.0 X-Spam-Status: No, score=-1.0 required=5.0 tests=ALL_TRUSTED,SHORTCIRCUIT shortcircuit=ham autolearn=disabled version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on canardo.mork.no X-Virus-Scanned: clamav-milter 0.102.4 at canardo X-Virus-Status: Clean X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20210313_165430_082935_63DA70D7 X-CRM114-Status: GOOD ( 19.58 ) X-Spam-Score: -2.5 (--) X-Spam-Report: Spam detection software, running on the system "desiato.infradead.org", has NOT identified this incoming email as spam. The original message has been attached to this so you can view it or label similar future email. If you have any questions, see the administrator of that system for details. Content preview: From: John Crispin 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. Content analysis details: (-2.5 points, 5.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- -2.3 RCVD_IN_DNSWL_MED RBL: Sender listed at https://www.dnswl.org/, medium trust [2001:4641:0:0:0:0:0:1 listed in] [list.dnswl.org] -0.0 SPF_HELO_PASS SPF: HELO matches SPF record -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain X-BeenThere: openwrt-devel@lists.openwrt.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: OpenWrt Development List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "openwrt-devel" Errors-To: openwrt-devel-bounces+incoming=patchwork.ozlabs.org@lists.openwrt.org From: John Crispin 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 Signed-off-by: John Crispin Signed-off-by: Bjørn Mork [commit message, release number] Tested-by: Stijn Segers Nacked-by: Rafał Miłecki --- "Adrian Schmutzler" 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 +}