diff mbox series

realtek-poe: add support for PoE on Realtek switches

Message ID 20210511152243.1167160-1-john@phrozen.org
State Under Review
Delegated to: John Crispin
Headers show
Series realtek-poe: add support for PoE on Realtek switches | expand

Commit Message

John Crispin May 11, 2021, 3:22 p.m. UTC
This patch adds a C rewrite of the LUA tool previously posted. With this new
daemon we map the DSA port names to the PSE port id. Support for several now
features was added, such as setting the priority and at/af mode. In the next
iteration addition port/mcu states will be exposed, aswell as adding support
for 4 wire mode and fault handling.

A Sample config looks like this:

config global
	option budget	'65'

config port
	option enable	'1'
	option id	'1'
	option name	'lan1'
	option poe_plus	'1'
	option priority	'2'

ubus call poe info
{
	"firmware": "v48.2",
	"mcu": "Nuvoton M05xx LAN Microcontroller",
	"budget": 65.000000,
	"consumption": 2.400000,
	"ports": {
		"lan1": {
			"priority": 2,
			"mode": "PoE",
			"status": "Delivering power",
			"consumption": 2.400000
		}
	}
}

Signed-off-by: John Crispin <john@phrozen.org>
---
 package/network/config/realtek-poe/Makefile   |  25 +
 .../config/realtek-poe/files/etc/config/poe   |   9 +
 .../config/realtek-poe/files/etc/init.d/poe   |  20 +
 .../config/realtek-poe/src/CMakeLists.txt     |  28 +
 package/network/config/realtek-poe/src/main.c | 844 ++++++++++++++++++
 5 files changed, 926 insertions(+)
 create mode 100644 package/network/config/realtek-poe/Makefile
 create mode 100644 package/network/config/realtek-poe/files/etc/config/poe
 create mode 100755 package/network/config/realtek-poe/files/etc/init.d/poe
 create mode 100644 package/network/config/realtek-poe/src/CMakeLists.txt
 create mode 100644 package/network/config/realtek-poe/src/main.c

Comments

Bjørn Mork May 11, 2021, 3:56 p.m. UTC | #1
John Crispin <john@phrozen.org> writes:

> This patch adds a C rewrite of the LUA tool previously posted. With this new
> daemon we map the DSA port names to the PSE port id. Support for several now
> features was added, such as setting the priority and at/af mode. In the next
> iteration addition port/mcu states will be exposed, aswell as adding support
> for 4 wire mode and fault handling.
>
> A Sample config looks like this:
>
> config global
> 	option budget	'65'
>
> config port
> 	option enable	'1'
> 	option id	'1'
> 	option name	'lan1'
> 	option poe_plus	'1'
> 	option priority	'2'
>
> ubus call poe info
> {
> 	"firmware": "v48.2",
> 	"mcu": "Nuvoton M05xx LAN Microcontroller",
> 	"budget": 65.000000,
> 	"consumption": 2.400000,
> 	"ports": {
> 		"lan1": {
> 			"priority": 2,
> 			"mode": "PoE",
> 			"status": "Delivering power",
> 			"consumption": 2.400000
> 		}
> 	}
> }


Tested-by: Bjørn Mork <bjorn@mork.no>

Running on a ZyXEL GS1900-10HP:

root@gs1900-10hp:~# ubus call poe info
{
        "firmware": "v22.4",
        "mcu": "ST Micro ST32F100 Microcontroller",
        "budget": 77.000000,
        "consumption": 7.700000,
        "ports": {
                "lan1": {
                        "priority": 0,
                        "mode": "PoE+",
                        "status": "Searching"
                },
                "lan2": {
                        "priority": 0,
                        "mode": "PoE+",
                        "status": "Searching"
                },
                "lan3": {
                        "priority": 0,
                        "mode": "PoE+",
                        "status": "Searching"
                },
                "lan4": {
                        "priority": 0,
                        "mode": "PoE+",
                        "status": "Searching"
                },
                "lan5": {
                        "priority": 0,
                        "mode": "PoE+",
                        "status": "Searching"
                },
                "lan6": {
                        "priority": 0,
                        "mode": "PoE+",
                        "status": "Searching"
                },
                "lan7": {
                        "priority": 0,
                        "mode": "PoE+",
                        "status": "Delivering power",
                        "consumption": 3.900000
                },
                "lan8": {
                        "priority": 0,
                        "mode": "PoE+",
                        "status": "Delivering power",
                        "consumption": 3.800000
                }
        }
}



Really nice stuff!
Rafał Miłecki May 11, 2021, 5:34 p.m. UTC | #2
On 11.05.2021 17:22, John Crispin wrote:
> This patch adds a C rewrite of the LUA tool previously posted. With this new
> daemon we map the DSA port names to the PSE port id. Support for several now
> features was added, such as setting the priority and at/af mode. In the next
> iteration addition port/mcu states will be exposed, aswell as adding support
> for 4 wire mode and fault handling.
> 
> A Sample config looks like this:
> 
> config global
> 	option budget	'65'
> 
> config port
> 	option enable	'1'
> 	option id	'1'
> 	option name	'lan1'
> 	option poe_plus	'1'
> 	option priority	'2'
> 
> ubus call poe info
> {
> 	"firmware": "v48.2",
> 	"mcu": "Nuvoton M05xx LAN Microcontroller",
> 	"budget": 65.000000,
> 	"consumption": 2.400000,
> 	"ports": {
> 		"lan1": {
> 			"priority": 2,
> 			"mode": "PoE",
> 			"status": "Delivering power",
> 			"consumption": 2.400000
> 		}
> 	}
> }

I'm happy to see C code, but this implementation still doesn't address
any of my comments I posted in the
Re: [PATCH v2] rtl83xx-poe: add package
https://patchwork.ozlabs.org/project/openwrt/patch/20210313165419.10713-1-bjorn@mork.no/#2666485

I strongly believe we need a more generic solution. A tiny framework
with generic PoE imlementation and then rtl83xx support on top of that.
John Crispin May 11, 2021, 5:36 p.m. UTC | #3
On 11.05.21 17:56, Bjørn Mork wrote:
> Tested-by: Bjørn Mork<bjorn@mork.no>
>
> Running on a ZyXEL GS1900-10HP:

thanks for testing. I just found 2 tiny issues that I already fixed 
locally and added a uci-defaults script s.T. we can setup a sane default 
uci for the various switches. If there are no further comments, I will 
merge this in the next couple of days

     John
John Crispin May 11, 2021, 5:38 p.m. UTC | #4
On 11.05.21 19:34, Rafał Miłecki wrote:
>
> I'm happy to see C code, but this implementation still doesn't address
> any of my comments I posted in the
> Re: [PATCH v2] rtl83xx-poe: add package
> https://patchwork.ozlabs.org/project/openwrt/patch/20210313165419.10713-1-bjorn@mork.no/#2666485 
>
>
> I strongly believe we need a more generic solution. A tiny framework
> with generic PoE imlementation and then rtl83xx support on top of that. 


agreed, but for now this makes PoE work for the community. the next step 
will be to move this into kernel space using the appropriate subsystems, 
however that will take time and effort.

the aim of the patch is to get support functional out of the box for the 
community and then address the real solution afterwards.

     John
John Crispin May 11, 2021, 5:43 p.m. UTC | #5
On 11.05.21 19:38, John Crispin wrote:
>
> On 11.05.21 19:34, Rafał Miłecki wrote:
>>
>> I'm happy to see C code, but this implementation still doesn't address
>> any of my comments I posted in the
>> Re: [PATCH v2] rtl83xx-poe: add package
>> https://patchwork.ozlabs.org/project/openwrt/patch/20210313165419.10713-1-bjorn@mork.no/#2666485 
>>
>>
>> I strongly believe we need a more generic solution. A tiny framework
>> with generic PoE imlementation and then rtl83xx support on top of that. 
>
>
> agreed, but for now this makes PoE work for the community. the next 
> step will be to move this into kernel space using the appropriate 
> subsystems, however that will take time and effort.
>
> the aim of the patch is to get support functional out of the box for 
> the community and then address the real solution afterwards.
>
>     John
>
that being said, I have a edgerouter-x(-sfp) on my desk. I already 
started a PoC patch to support a more generic approach for PoE that is 
agnostic of wheter the HW support is driven via GPIO or a ttyS attached 
MCU. but as I said, takes time and effort. for now, the RTL crowd will 
have PoE and 1-2 months down the line we will have a more generic 
approach... any other PoE capable HW around that we need to consider in 
the design phase ?

     John
Rafał Miłecki May 11, 2021, 5:45 p.m. UTC | #6
On 11.05.2021 19:38, John Crispin wrote:
> On 11.05.21 19:34, Rafał Miłecki wrote:
>>
>> I'm happy to see C code, but this implementation still doesn't address
>> any of my comments I posted in the
>> Re: [PATCH v2] rtl83xx-poe: add package
>> https://patchwork.ozlabs.org/project/openwrt/patch/20210313165419.10713-1-bjorn@mork.no/#2666485
>>
>> I strongly believe we need a more generic solution. A tiny framework
>> with generic PoE imlementation and then rtl83xx support on top of that. 
> 
> 
> agreed, but for now this makes PoE work for the community. the next step will be to move this into kernel space using the appropriate subsystems, however that will take time and effort.
> 
> the aim of the patch is to get support functional out of the box for the community and then address the real solution afterwards.

This never works. When we commit hacks like this to the repo they stay
for years or for ever. Once you commit this we'll see uci defaults for
PoE. Then LuCI module. Cleaning that up later will require a lot more
planning to avoid breaking existing setups.

Developing this as a tiny subsystem really won't take much more time.
How much can it be? Two days?

I'm really tired of dealing with such hacky solutions. Look at netifd,
DSA and LuCI as example. We ended up with something that noone fully
understands. Jo - our UI guru - couldn't deal with designing a proper
UI for that f*cked config syntax.
John Crispin May 11, 2021, 5:46 p.m. UTC | #7
On 11.05.21 19:45, Rafał Miłecki wrote:
> I'm really tired of dealing with such hacky solutions. Look at netifd,
> DSA and LuCI as example. We ended up with something that noone fully
> understands. Jo - our UI guru - couldn't deal with designing a proper
> UI for that f*cked config syntax. 

sorry mate, I am not the reason you are having a bad day, so dont vent 
it out on me.

     John
Rafał Miłecki May 11, 2021, 5:51 p.m. UTC | #8
On 11.05.2021 19:46, John Crispin wrote:
> On 11.05.21 19:45, Rafał Miłecki wrote:
>> I'm really tired of dealing with such hacky solutions. Look at netifd,
>> DSA and LuCI as example. We ended up with something that noone fully
>> understands. Jo - our UI guru - couldn't deal with designing a proper
>> UI for that f*cked config syntax. 
> 
> sorry mate, I am not the reason you are having a bad day, so dont vent it out on me.

It's just an example of shitty situation we got. Nothing more.

I took a good amount of time to provide valuable review for the
[PATCH v2] rtl83xx-poe: add package

I described problems, missing parts, provided examples how one can
solve it. Let's discuss that please. Instead of commenting on one
example I provided.
John Crispin May 11, 2021, 5:54 p.m. UTC | #9
On 11.05.21 19:51, Rafał Miłecki wrote:
> On 11.05.2021 19:46, John Crispin wrote:
>> On 11.05.21 19:45, Rafał Miłecki wrote:
>>> I'm really tired of dealing with such hacky solutions. Look at netifd,
>>> DSA and LuCI as example. We ended up with something that noone fully
>>> understands. Jo - our UI guru - couldn't deal with designing a proper
>>> UI for that f*cked config syntax. 
>>
>> sorry mate, I am not the reason you are having a bad day, so dont 
>> vent it out on me.
>
> It's just an example of shitty situation we got. Nothing more.
>
> I took a good amount of time to provide valuable review for the
> [PATCH v2] rtl83xx-poe: add package
>
> I described problems, missing parts, provided examples how one can
> solve it. Let's discuss that please. Instead of commenting on one
> example I provided.

lulz, why so aggravated ... its about the code not the person ...

possibly I missed some of your feedback, will review tomorrow, keep the 
spirit mate and just chill a little ...

     John
Birger Koblitz May 11, 2021, 7:06 p.m. UTC | #10
On 11/05/2021 19:51, Rafał Miłecki wrote:
> On 11.05.2021 19:46, John Crispin wrote:
>> On 11.05.21 19:45, Rafał Miłecki wrote:
>>> I'm really tired of dealing with such hacky solutions. Look at netifd,
>>> DSA and LuCI as example. We ended up with something that noone fully
>>> understands. Jo - our UI guru - couldn't deal with designing a proper
>>> UI for that f*cked config syntax. 
>>
>> sorry mate, I am not the reason you are having a bad day, so dont vent it out on me.
> 
> It's just an example of shitty situation we got. Nothing more.
> 
> I took a good amount of time to provide valuable review for the
> [PATCH v2] rtl83xx-poe: add package
> 
> I described problems, missing parts, provided examples how one can
> solve it. Let's discuss that please. Instead of commenting on one
> example I provided.
> 
I think the growing RTL83xx crowd has shown that we are able to refactor
code and strive for the good stuff, see all the drivers that are now
getting into mainline. I also don't think we are getting into any kind
of cul-de-sac we cannot get out, because also other platforms will make
use of the uart-based interface these PoE chips offer, so I do not see
that there would be any need for major re-designs, and in particular no
need to implement this in kernel space. If for some reasons there are
PoE solutions that have a similar command-set but different (GPIO) interface
then if necessary this could be handled by a separate kernel module
that would offer a uart interface for the user-space. Alternatively
we go to a different user-space interface altogether, but it is clear
a user-space solution which talks PoE commands will be needed.

Birger
Petr Štetiar May 12, 2021, 9:09 a.m. UTC | #11
John Crispin <john@phrozen.org> [2021-05-11 17:22:43]:

Hi,

>  package/network/config/realtek-poe/src/main.c | 844 ++++++++++++++++++

I would prefer to have this as out of tree project, so we could add CI
pipeline to it for improved QA.

> +struct port_config {
> +	char name[16];

It would be nice to get rid of all those magic numbers as well, there is a lot
of them.

Ideally the command bytes offsets should be defined (via some enum?) as well,
so it's easier to review the code otherwise one would need to keep in head,
that byte 11 is CRC etc.

> +	fprintf(stderr, "%s ->", type);
> +	for (i = 0; i < 12; i++)
> +		fprintf(stderr, " 0x%02x", data[i]);
> +	fprintf(stderr, "\n");

ULOG_DBG ?

> +	config.port_count = reply[3];

this should be checked for > MAX_PORT

> +	state.ports[reply[2]].poe_mode = GET_STR(reply[3], mode);

reply[2] should be checked for > MAX_PORT

> +	for (i = 0; i < 8; i++)

i < MAX_PORT

> +	state.ports[reply[2]].watt = watt;

reply[2] should be checked for > MAX_PORT

> +	if (ret)
> +		fprintf(stderr, "Failed to add object: %s\n", ubus_strerror(ret));

ULOG_ERR ?

-- ynezz
Petr Štetiar May 12, 2021, 9:11 a.m. UTC | #12
John Crispin <john@phrozen.org> [2021-05-11 19:43:09]:

> that being said, I have a edgerouter-x(-sfp) on my desk. I already started a
> PoC patch to support a more generic approach for PoE that is agnostic of
> wheter the HW support is driven via GPIO or a ttyS attached MCU.

Then you should consider renaming it to something generic as well, upoe(d) or
such.

-- ynezz
Rafał Miłecki May 12, 2021, 9:14 p.m. UTC | #13
On 11.05.2021 19:54, John Crispin wrote:
> On 11.05.21 19:51, Rafał Miłecki wrote:
>> On 11.05.2021 19:46, John Crispin wrote:
>>> On 11.05.21 19:45, Rafał Miłecki wrote:
>>>> I'm really tired of dealing with such hacky solutions. Look at netifd,
>>>> DSA and LuCI as example. We ended up with something that noone fully
>>>> understands. Jo - our UI guru - couldn't deal with designing a proper
>>>> UI for that f*cked config syntax. 
>>>
>>> sorry mate, I am not the reason you are having a bad day, so dont vent it out on me.
>>
>> It's just an example of shitty situation we got. Nothing more.
>>
>> I took a good amount of time to provide valuable review for the
>> [PATCH v2] rtl83xx-poe: add package
>>
>> I described problems, missing parts, provided examples how one can
>> solve it. Let's discuss that please. Instead of commenting on one
>> example I provided.
> 
> lulz, why so aggravated ... its about the code not the person ...
> 
> possibly I missed some of your feedback, will review tomorrow, keep the spirit mate and just chill a little ...

Apparently I've used too strong words, I'm sorry for that, that was
unintentional. I was trying to provide strong arguments and f*kup
example in a good faith, but as I understand it, it sounded rude.

I'd like to help adding PoE support in a clean way from the beginning,
I'll come with some ideas and maybe a code later this week. Please hold
on a bit with commiting this code, I hope we can refactor it a bit in a
reasonable time.
Paul Fertser Sept. 22, 2021, 10:13 p.m. UTC | #14
Hello John,

Thank you for the rather useful tool. One comment below.

On Tue, May 11, 2021 at 05:22:43PM +0200, John Crispin wrote:
> +/* 0x17 - Set power management mode
> + *	0: None (No Power Management mode) (Default in Semi-Auto mode)
> + *	1: Static Power Management with Port Priority(Default in Automode)
> + *	2: Dynamic Power Management with Port Priority
> + *	3: Static Power Management without Port Priority
> + *	4: Dynamic Power Management without Port Priority
> + */
> +static int
> +poe_cmd_power_mgmt_mode(unsigned char mode)
> +{
> +	unsigned char cmd[] = { 0x18, 0x00, mode };

Typo in command number here.

Because of it on D-Link DGS-1210-10P R1 with

        "firmware": "v24.33",
        "mcu": "Nuvoton M05xx LAN Microcontroller",

the power is never provided, the port status changes between "unknown"
(6):
RX -> 0x2a 0x63 0x00 0x11 0xb6 0x11 0x11 0x11 0x11 0x11 0x11 0xba
and "Searching" (1) when I plug and unplug the cable.
Bjørn Mork Sept. 25, 2021, 10:01 p.m. UTC | #15
And then we had https://github.com/blocktrron/poemgr

But without support for the MCU controlled broadcom PSE chips in the
realtek target, and without ubus support.

Time to consolidate some of this code?  Or is poemgr the result of that,
just not yet with those features implmented?


Bjørn
Raylynn Knight Sept. 26, 2021, 5:22 a.m. UTC | #16
The sender domain has a DMARC Reject/Quarantine policy which disallows
sending mailing list messages using the original "From" header.

To mitigate this problem, the original message has been wrapped
automatically by the mailing list software.
It’s not clear to me what OpenWrt supported device(s) the below poemgr is meant for!

It might be helpful to compile a list of currently supported OpenWrt devices that have PoE capability.
Below would be my contribution of OpenWrt supported devices that I know have PoE capability:

Device								Processor	PoE out
——————————————————————————————————
D-Link DGS-1210-10P					RTL8380M	8x 802.3af/at
develo WiFi pro 1750e					QCA9558	1x 802.3af
MikroTik RB750P-PBr2					QCA9533	4x Passive PoE
MikroTik RB750UPr2  (hEX PoE lite)		QCA9531    	4x Passive PoE
MikroTik RB2011iL-IN	        			AR9344		1x Passive PoE
MikroTik RBcAPGi      (cAP ac)			IPQ4018		1x Passive PoE
MikroTik RB962UiGS (hAP ac)			QCA9558	1x Passive PoE
MikroTik RBmAP-2nD (wAP)          		QCA9531	1x Passive PoE
Netgear GS110TPP					RTL8380M	8x 802.3af/at
Netgear GS310TPv1					RTL8380M	8x 802.3af/at
TP-Link TPE210						AR9344		1x Passive PoE
TP-Link TEP510						AR9350		1x Passive PoE
Ubiquiti EdgeRouter 6P					CN7130		5x Passive PoE
Ubiquiti EdgeRouter X					MT7621AT	1x Passive PoE
Ubiquiti EdgeRouter X SFP				MT7621AT	5x Passive PoE
Ubiquiti ToughSwitch TS-5-POE			AR7240		5x Passive PoE
Ubiquiti EdgePoint R6					MT7621AT	5x Passive PoE
ZyXEL GS1900-10HP					RTL8380M	8x 802.3af/at
ZyXEL GS1900-8HP					RTL8380M	8x 802.3af/at
ZyXEL GS1900-8HP					RTL8380M      8x802.3af/at
ZyXEL GS1900-24HP v2				RTL8382M	24x 802.3af/at

I actually have the D-Link switch, 1 of the Netgear switches, 4 of the Ubiquiti devices and 2 of the ZyXEL switches.
I also have additional realtek based PoE switches I plan on submitting support for when I get time.

Ray

> On Sep 25, 2021, at 6:01 PM, Bjørn Mork <bjorn@mork.no> wrote:
> 
> And then we had https://github.com/blocktrron/poemgr
> 
> But without support for the MCU controlled broadcom PSE chips in the
> realtek target, and without ubus support.
> 
> Time to consolidate some of this code?  Or is poemgr the result of that,
> just not yet with those features implmented?
> 
> 
> Bjørn
> 
> _______________________________________________
> openwrt-devel mailing list
> openwrt-devel@lists.openwrt.org
> https://lists.openwrt.org/mailman/listinfo/openwrt-devel
John Crispin Sept. 26, 2021, 5:23 a.m. UTC | #17
poemgr does not use the APIs provided by libubox so I doubt that this 
will land in the tree.

     John

On 26.09.21 07:22, Raylynn Knight wrote:
> It’s not clear to me what OpenWrt supported device(s) the below poemgr is meant for!
>
> It might be helpful to compile a list of currently supported OpenWrt devices that have PoE capability.
> Below would be my contribution of OpenWrt supported devices that I know have PoE capability:
>
> Device								Processor	PoE out
> ——————————————————————————————————
> D-Link DGS-1210-10P					RTL8380M	8x 802.3af/at
> develo WiFi pro 1750e					QCA9558	1x 802.3af
> MikroTik RB750P-PBr2					QCA9533	4x Passive PoE
> MikroTik RB750UPr2  (hEX PoE lite)		QCA9531    	4x Passive PoE
> MikroTik RB2011iL-IN	        			AR9344		1x Passive PoE
> MikroTik RBcAPGi      (cAP ac)			IPQ4018		1x Passive PoE
> MikroTik RB962UiGS (hAP ac)			QCA9558	1x Passive PoE
> MikroTik RBmAP-2nD (wAP)          		QCA9531	1x Passive PoE
> Netgear GS110TPP					RTL8380M	8x 802.3af/at
> Netgear GS310TPv1					RTL8380M	8x 802.3af/at
> TP-Link TPE210						AR9344		1x Passive PoE
> TP-Link TEP510						AR9350		1x Passive PoE
> Ubiquiti EdgeRouter 6P					CN7130		5x Passive PoE
> Ubiquiti EdgeRouter X					MT7621AT	1x Passive PoE
> Ubiquiti EdgeRouter X SFP				MT7621AT	5x Passive PoE
> Ubiquiti ToughSwitch TS-5-POE			AR7240		5x Passive PoE
> Ubiquiti EdgePoint R6					MT7621AT	5x Passive PoE
> ZyXEL GS1900-10HP					RTL8380M	8x 802.3af/at
> ZyXEL GS1900-8HP					RTL8380M	8x 802.3af/at
> ZyXEL GS1900-8HP					RTL8380M      8x802.3af/at
> ZyXEL GS1900-24HP v2				RTL8382M	24x 802.3af/at
>
> I actually have the D-Link switch, 1 of the Netgear switches, 4 of the Ubiquiti devices and 2 of the ZyXEL switches.
> I also have additional realtek based PoE switches I plan on submitting support for when I get time.
>
> Ray
>
>> On Sep 25, 2021, at 6:01 PM, Bjørn Mork <bjorn@mork.no> wrote:
>>
>> And then we had https://github.com/blocktrron/poemgr
>>
>> But without support for the MCU controlled broadcom PSE chips in the
>> realtek target, and without ubus support.
>>
>> Time to consolidate some of this code?  Or is poemgr the result of that,
>> just not yet with those features implmented?
>>
>>
>> Bjørn
>>
>> _______________________________________________
>> openwrt-devel mailing list
>> openwrt-devel@lists.openwrt.org
>> https://lists.openwrt.org/mailman/listinfo/openwrt-devel
Bjørn Mork Sept. 26, 2021, 8:43 a.m. UTC | #18
John Crispin <john@phrozen.org> writes:

> poemgr does not use the APIs provided by libubox so I doubt that this
> will land in the tree.

It's going through the packages repo according to
https://github.com/openwrt/openwrt/commit/a9839697896c4fdf8c44a06bbce466ce52493069

The distinction is of little imprtance to the outside world.  You will
have poemgr in OpenWrt.



Bjørn
Bjørn Mork Sept. 26, 2021, 8:51 a.m. UTC | #19
Raylynn Knight <rayknight@me.com> writes:

> It’s not clear to me what OpenWrt supported device(s) the below poemgr
> is meant for!

The Ubiquiti USW-Flex.  And maybe other devices.  David will know.  I
just noticed it from the recent commit in master.

Looks like a really nice little MT7621 device with 802.3af/at/bt in and
4 x 802.3af out. Obviously without much budget if powered by 802.3af,
but that's still a docuemnted and supported configuration!

> It might be helpful to compile a list of currently supported OpenWrt
> devices that have PoE capability.  Below would be my contribution of
> OpenWrt supported devices that I know have PoE capability:
>
> Device								Processor	PoE out
> ——————————————————————————————————
> D-Link DGS-1210-10P					RTL8380M	8x 802.3af/at
> develo WiFi pro 1750e					QCA9558	1x 802.3af
> MikroTik RB750P-PBr2					QCA9533	4x Passive PoE
> MikroTik RB750UPr2  (hEX PoE lite)		QCA9531    	4x Passive PoE
> MikroTik RB2011iL-IN	        			AR9344		1x Passive PoE
> MikroTik RBcAPGi      (cAP ac)			IPQ4018		1x Passive PoE
> MikroTik RB962UiGS (hAP ac)			QCA9558	1x Passive PoE
> MikroTik RBmAP-2nD (wAP)          		QCA9531	1x Passive PoE
> Netgear GS110TPP					RTL8380M	8x 802.3af/at
> Netgear GS310TPv1					RTL8380M	8x 802.3af/at
> TP-Link TPE210						AR9344		1x Passive PoE
> TP-Link TEP510						AR9350		1x Passive PoE
> Ubiquiti EdgeRouter 6P					CN7130		5x Passive PoE
> Ubiquiti EdgeRouter X					MT7621AT	1x Passive PoE
> Ubiquiti EdgeRouter X SFP				MT7621AT	5x Passive PoE
> Ubiquiti ToughSwitch TS-5-POE			AR7240		5x Passive PoE
> Ubiquiti EdgePoint R6					MT7621AT	5x Passive PoE
> ZyXEL GS1900-10HP					RTL8380M	8x 802.3af/at
> ZyXEL GS1900-8HP					RTL8380M	8x 802.3af/at
> ZyXEL GS1900-8HP					RTL8380M      8x802.3af/at
> ZyXEL GS1900-24HP v2				RTL8382M	24x 802.3af/at
>
> I actually have the D-Link switch, 1 of the Netgear switches, 4 of the
> Ubiquiti devices and 2 of the ZyXEL switches.  I also have additional
> realtek based PoE switches I plan on submitting support for when I get
> time.

Great list!  Can't add anything morte than the USW-Flex here.  I only
have two GS1900-10HP myself


Bjørn
David Bauer Sept. 26, 2021, 11:34 a.m. UTC | #20
Hi Bjorn,
Hi John,

On 9/26/21 10:43, Bjørn Mork wrote:
> John Crispin <john@phrozen.org> writes:
> 
>> poemgr does not use the APIs provided by libubox so I doubt that this
>> will land in the tree.
> 
> It's going through the packages repo according to
> https://github.com/openwrt/openwrt/commit/a9839697896c4fdf8c44a06bbce466ce52493069
> 
> The distinction is of little imprtance to the outside world.  You will
> have poemgr in OpenWrt.

poemgr is set to land in the packages feed. I don't have stakes in the realtek PoE business
and poemgr is more or less the result of a weekend hacking around the specific device (USW-Lite).

I didn't fully catch up what the ultimate goal around PoE support is, but having the ability to
control the ports in the first place being it with a non-preinstalled tool from the packages repo
is a good start where we can iterate upon and either integrate support for an additional PSE controller
with it's quirks or aim for an extension of a kernel subsystem.

That being set, the goal of poemgr is not to replace the other efforts. I'm sorry if I caused this impression.

Best
David

> 
> 
> 
> Bjørn
>
John Crispin Sept. 26, 2021, 12:31 p.m. UTC | #21
On 26.09.21 13:34, David Bauer wrote:
> That being set, the goal of poemgr is not to replace the other 
> efforts. I'm sorry if I caused this impression. 

it did not replace anything. I am fully with you on this one, 
realtek-poe took the same approach but merging it was NAK'ed with the 
argument, that we can only merge the ultimate solution. which is why 
till this day there is no upstream support for realtek.

     John
Bjørn Mork Sept. 26, 2021, 12:39 p.m. UTC | #22
David Bauer <mail@david-bauer.net> writes:

> That being set, the goal of poemgr is not to replace the other
> efforts. I'm sorry if I caused this impression.

Not my intention to complain at all.  Just wanted those with a stake in
this to be aware of your work.

Thanks a lot for the efforts you have done to add complete support for
this very interesting device. As well as many other devices.  Connected
to a Unifi 6 lite right now, so I do appreciate that work!

And having these two implementations for widely different PoE systems is
extremely useful for further "poe manager" design discussions.

What I would want to avoid by posting is that we end up having two
separate poe managers in OpenWrt (including "packages") forever... Which
is how things look right now.. IMHO it would be better to combine them
before mergin.  Or at least agree on one of them as a basis for further
developement and discussion, so we can avoid adding a userspace API
which is doomed to go away soon.


Bjørn
David Bauer Sept. 26, 2021, 3:18 p.m. UTC | #23
Hi John,

On 9/26/21 14:31, John Crispin wrote:
> 
> On 26.09.21 13:34, David Bauer wrote:
>> That being set, the goal of poemgr is not to replace the other efforts. I'm sorry if I caused this impression. 
> 
> it did not replace anything. I am fully with you on this one, realtek-poe took the same approach but merging it was NAK'ed with the argument, that we can only merge the ultimate solution. which is why till this day there is no upstream support for realtek.

 From my perspective, providing both for the time being via the packages feed (not preinstalled)
is a good-enough solution. Otherwise, iterating over the project with multiple people starts to become
difficult, as multiple forks (each fixing one issue / adding one feature) start to come up and
bringing them together becomes harder and harder.

Having it not in the core repository should indicate enough that it is not a definitive solution.

Maybe I'm bringing up points which are already off the list, but at least i can try :)

Best
David

> 
>      John
> 
> 
> _______________________________________________
> openwrt-devel mailing list
> openwrt-devel@lists.openwrt.org
> https://lists.openwrt.org/mailman/listinfo/openwrt-devel
diff mbox series

Patch

diff --git a/package/network/config/realtek-poe/Makefile b/package/network/config/realtek-poe/Makefile
new file mode 100644
index 0000000000..4dba054bb2
--- /dev/null
+++ b/package/network/config/realtek-poe/Makefile
@@ -0,0 +1,25 @@ 
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=realtek-poe
+PKG_RELEASE:=1
+
+PKG_LICENSE:=GPL-2.0
+PKG_MAINTAINER:=John Crispin <john@phrozen.org>
+
+include $(INCLUDE_DIR)/package.mk
+include $(INCLUDE_DIR)/cmake.mk
+
+define Package/realtek-poe
+  SECTION:=net
+  CATEGORY:=Network
+  TITLE:=Realtek PoE Switch Port daemon
+  DEPENDS:=@TARGET_realtek +libubox +libubus +libuci
+endef
+
+define Package/realtek-poe/install
+	$(INSTALL_DIR) $(1)/usr/bin
+	$(INSTALL_BIN) $(PKG_BUILD_DIR)/realtek-poe $(1)/usr/bin/
+	$(CP) ./files/* $(1)
+endef
+
+$(eval $(call BuildPackage,realtek-poe))
diff --git a/package/network/config/realtek-poe/files/etc/config/poe b/package/network/config/realtek-poe/files/etc/config/poe
new file mode 100644
index 0000000000..6ef9d40ad2
--- /dev/null
+++ b/package/network/config/realtek-poe/files/etc/config/poe
@@ -0,0 +1,9 @@ 
+config global
+	option budget	'65'
+
+config port
+	option enable	'1'
+	option id	'1'
+	option name	'lan1'
+	option poe_plus	'1'
+	option priority	'2'
diff --git a/package/network/config/realtek-poe/files/etc/init.d/poe b/package/network/config/realtek-poe/files/etc/init.d/poe
new file mode 100755
index 0000000000..66f77d6ef9
--- /dev/null
+++ b/package/network/config/realtek-poe/files/etc/init.d/poe
@@ -0,0 +1,20 @@ 
+#!/bin/sh /etc/rc.common
+
+START=80
+USE_PROCD=1
+PROG=/usr/bin/realtek-poe
+
+reload_service() {
+	ubus call poe reload
+}
+
+service_triggers() {                                
+        procd_add_config_trigger "config.change" "poe" ubus call poe reload
+}
+
+start_service() {
+	procd_open_instance
+	procd_set_param command "$PROG"
+	procd_set_param respawn
+	procd_close_instance
+}
diff --git a/package/network/config/realtek-poe/src/CMakeLists.txt b/package/network/config/realtek-poe/src/CMakeLists.txt
new file mode 100644
index 0000000000..4eb81f4577
--- /dev/null
+++ b/package/network/config/realtek-poe/src/CMakeLists.txt
@@ -0,0 +1,28 @@ 
+cmake_minimum_required(VERSION 2.6)
+
+PROJECT(realtek-poe C)
+ADD_DEFINITIONS(-Os -ggdb -Wextra -Wall -Werror --std=gnu99 -Wmissing-declarations -Wno-unused-parameter)
+
+SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
+
+SET(SOURCES main.c)
+
+IF(DEBUG)
+  ADD_DEFINITIONS(-DDEBUG -g3)
+ENDIF()
+
+FIND_LIBRARY(ubus NAMES ubus)
+FIND_LIBRARY(uci NAMES uci)
+FIND_LIBRARY(ubox NAMES ubox)
+FIND_PATH(uci_include_dir NAMES uci.h)
+FIND_PATH(ubus_include_dir NAMES libubus.h)
+FIND_PATH(ubox_include_dir NAMES libubox/usock.h)
+INCLUDE_DIRECTORIES(${ubox_include_dir} ${ubus_include_dir} ${uci_include_dir})
+
+ADD_EXECUTABLE(realtek-poe ${SOURCES})
+
+TARGET_LINK_LIBRARIES(realtek-poe ${ubox} ${ubus} ${uci})
+
+INSTALL(TARGETS realtek-poe
+	RUNTIME DESTINATION sbin
+)
diff --git a/package/network/config/realtek-poe/src/main.c b/package/network/config/realtek-poe/src/main.c
new file mode 100644
index 0000000000..034ac27432
--- /dev/null
+++ b/package/network/config/realtek-poe/src/main.c
@@ -0,0 +1,844 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <termios.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <getopt.h>
+
+#include <libubox/ustream.h>
+#include <libubox/uloop.h>
+#include <libubox/list.h>
+#include <libubox/ulog.h>
+#include <libubus.h>
+
+#include <uci.h>
+#include <uci_blob.h>
+
+#define ULOG_DBG(fmt, ...) ulog(LOG_DEBUG, fmt, ## __VA_ARGS__)
+
+typedef int (*poe_reply_handler)(unsigned char *reply);
+
+#define MAX_PORT	8
+#define GET_STR(a, b)	(a < ARRAY_SIZE(b) ? b[a] : NULL)
+
+struct port_config {
+	char name[16];
+	unsigned char enable;
+	unsigned char priority;
+	unsigned char power_up_mode;
+	unsigned char power_budget;
+};
+
+struct config {
+	int debug;
+
+	int budget;
+	int budget_guard;
+
+	int port_count;
+	struct port_config ports[MAX_PORT];
+};
+
+struct port_state {
+	char *status;
+	float watt;
+	char *poe_mode;
+};
+
+struct state {
+	char *sys_mode;
+	unsigned char sys_version;
+	char *sys_mcu;
+	char *sys_status;
+	unsigned char sys_ext_version;
+	float power_consumption;
+
+	struct port_state ports[MAX_PORT];
+};
+
+struct cmd {
+	struct list_head list;
+	unsigned char cmd[12];
+};
+
+static struct uloop_timeout state_timeout;
+static struct ubus_auto_conn conn;
+static struct ustream_fd stream;
+static LIST_HEAD(cmd_pending);
+static unsigned char cmd_seq;
+static struct state state;
+static struct blob_buf b;
+
+static struct config config = {
+	.budget = 65,
+	.budget_guard = 7,
+	.port_count = 8,
+};
+
+static void
+config_load_port(struct uci_section *s)
+{
+	enum {
+		PORT_ATTR_ID,
+		PORT_ATTR_NAME,
+		PORT_ATTR_ENABLE,
+		PORT_ATTR_PRIO,
+		PORT_ATTR_POE_PLUS,
+		__PORT_ATTR_MAX,
+	};
+
+	static const struct blobmsg_policy port_attrs[__PORT_ATTR_MAX] = {
+		[PORT_ATTR_ID] = { .name = "id", .type = BLOBMSG_TYPE_INT32 },
+		[PORT_ATTR_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING },
+		[PORT_ATTR_ENABLE] = { .name = "enable", .type = BLOBMSG_TYPE_INT32 },
+		[PORT_ATTR_PRIO] = { .name = "priority", .type = BLOBMSG_TYPE_INT32 },
+		[PORT_ATTR_POE_PLUS] = { .name = "poe_plus", .type = BLOBMSG_TYPE_INT32 },
+	};
+
+	const struct uci_blob_param_list port_attr_list = {
+		.n_params = __PORT_ATTR_MAX,
+		.params = port_attrs,
+	};
+
+	struct blob_attr *tb[__PORT_ATTR_MAX] = { 0 };
+	unsigned int id;
+
+	blob_buf_init(&b, 0);
+	uci_to_blob(&b, s, &port_attr_list);
+	blobmsg_parse(port_attrs, __PORT_ATTR_MAX, tb, blob_data(b.head), blob_len(b.head));
+
+	if (!tb[PORT_ATTR_ID] || !tb[PORT_ATTR_NAME]) {
+		ULOG_ERR("invalid port settings");
+		return;
+	}
+
+	id = blobmsg_get_u32(tb[PORT_ATTR_ID]);
+	if (!id || id > MAX_PORT) {
+		ULOG_ERR("invalid port id");
+		return;
+	}
+	id--;
+
+	strncpy(config.ports[id].name, blobmsg_get_string(tb[PORT_ATTR_NAME]), 16);
+
+	if (tb[PORT_ATTR_ENABLE])
+		config.ports[id].enable = !!blobmsg_get_u32(tb[PORT_ATTR_ENABLE]);
+
+	if (tb[PORT_ATTR_PRIO])
+		config.ports[id].priority = blobmsg_get_u32(tb[PORT_ATTR_PRIO]);
+	if (config.ports[id].priority > 3)
+		config.ports[id].priority = 3;
+
+	if (tb[PORT_ATTR_POE_PLUS] && blobmsg_get_u32(tb[PORT_ATTR_POE_PLUS]))
+		config.ports[id].power_up_mode = 3;
+}
+
+static void
+config_load_global(struct uci_section *s)
+{
+	enum {
+		GLOBAL_ATTR_BUDGET,
+		GLOBAL_ATTR_GUARD,
+		__GLOBAL_ATTR_MAX,
+	};
+
+	static const struct blobmsg_policy global_attrs[__GLOBAL_ATTR_MAX] = {
+		[GLOBAL_ATTR_BUDGET] = { .name = "budget", .type = BLOBMSG_TYPE_INT32 },
+		[GLOBAL_ATTR_GUARD] = { .name = "guard", .type = BLOBMSG_TYPE_INT32 },
+	};
+
+	const struct uci_blob_param_list global_attr_list = {
+		.n_params = __GLOBAL_ATTR_MAX,
+		.params = global_attrs,
+	};
+
+	struct blob_attr *tb[__GLOBAL_ATTR_MAX] = { 0 };
+
+	blob_buf_init(&b, 0);
+	uci_to_blob(&b, s, &global_attr_list);
+	blobmsg_parse(global_attrs, __GLOBAL_ATTR_MAX, tb, blob_data(b.head), blob_len(b.head));
+
+	config.budget = 65;
+	if (tb[GLOBAL_ATTR_BUDGET])
+		config.budget = blobmsg_get_u32(tb[GLOBAL_ATTR_BUDGET]);
+
+	if (tb[GLOBAL_ATTR_GUARD])
+		config.budget_guard = blobmsg_get_u32(tb[GLOBAL_ATTR_GUARD]);
+	else
+		config.budget_guard = config.budget / 10;
+}
+
+static void
+config_load(int init)
+{
+	struct uci_context *uci = uci_alloc_context();
+        struct uci_package *package = NULL;
+
+	memset(config.ports, 0, sizeof(config.ports));
+
+	if (!uci_load(uci, "poe", &package)) {
+		struct uci_element *e;
+
+		if (init)
+			uci_foreach_element(&package->sections, e) {
+				struct uci_section *s = uci_to_section(e);
+
+				if (!strcmp(s->type, "global"))
+					config_load_global(s);
+			}
+		uci_foreach_element(&package->sections, e) {
+			struct uci_section *s = uci_to_section(e);
+
+			if (!strcmp(s->type, "port"))
+				config_load_port(s);
+		}
+	}
+
+	uci_unload(uci, package);
+	uci_free_context(uci);
+}
+
+static void
+poe_cmd_dump(char *type, unsigned char *data)
+{
+	int i;
+
+	if (!config.debug)
+		return;
+
+	fprintf(stderr, "%s ->", type);
+	for (i = 0; i < 12; i++)
+		fprintf(stderr, " 0x%02x", data[i]);
+	fprintf(stderr, "\n");
+}
+
+static int
+poe_cmd_send(struct cmd *cmd)
+{
+	poe_cmd_dump("TX", cmd->cmd);
+	ustream_write(&stream.stream, (char *)cmd->cmd, 12, false);
+
+	return 0;
+}
+
+static int
+poe_cmd_next(void)
+{
+	struct cmd *cmd;
+
+	if (list_empty(&cmd_pending))
+		return -1;
+
+	cmd = list_first_entry(&cmd_pending, struct cmd, list);
+
+	return poe_cmd_send(cmd);
+}
+
+static int
+poe_cmd_queue(unsigned char *_cmd, int len)
+{
+	int i, empty = list_empty(&cmd_pending);
+	struct cmd *cmd = malloc(sizeof(*cmd));
+
+	memset(cmd, 0, sizeof(*cmd));
+	memset(cmd->cmd, 0xff, 12);
+	memcpy(cmd->cmd, _cmd, len);
+
+	cmd_seq++;
+	cmd->cmd[1] = cmd_seq;
+	cmd->cmd[11] = 0;
+
+	for (i = 0; i < 11; i++)
+		cmd->cmd[11] += cmd->cmd[i];
+
+	list_add_tail(&cmd->list, &cmd_pending);
+
+	if (empty)
+		return poe_cmd_send(cmd);
+
+	return 0;
+}
+
+/* 0x00 - Set port enable
+ *	0: Disable
+ *	1: Enable
+ */
+static int
+poe_cmd_port_enable(unsigned char port, unsigned char enable)
+{
+	unsigned char cmd[] = { 0x00, 0x00, port, enable };
+
+	return poe_cmd_queue(cmd, sizeof(cmd));
+}
+
+/* 0x06 - Set global port enable
+ *	0: Disable PSE Functionality on all Ports
+ *	1: Enable PSE Functionality on all Ports
+ *	2: Enable Force power Functionality on all ports
+ *	3: Enable Force Power with Disconnect Functionality on all Ports
+ */
+static int
+poe_cmd_global_port_enable(unsigned char enable)
+{
+	unsigned char cmd[] = { 0x06, 0x00, enable };
+
+	return poe_cmd_queue(cmd, sizeof(cmd));
+}
+
+/* 0x10 - Set port detection type
+ *	1: Legacy Capacitive Detection only
+ *	2: IEEE 802.3af 4-Point Detection only (Default)
+ *	3: IEEE 802.3af 4-Point followed by Legacy
+ *	4: IEEE 802.3af 2-Point detection (Not Supported)
+ *	5: IEEE 802.3af 2-Point followed by Legacy
+ */
+static int
+poe_cmd_port_detection_type(unsigned char port, unsigned char type)
+{
+	unsigned char cmd[] = { 0x10, 0x00, port, type };
+
+	return poe_cmd_queue(cmd, sizeof(cmd));
+}
+
+/* 0x11 - Set port classification
+ *	0: Disable
+ *	1: Enable
+ */
+static int
+poe_cmd_port_classification(unsigned char port, unsigned char classification)
+{
+	unsigned char cmd[] = { 0x11, 0x00, port, classification };
+
+	return poe_cmd_queue(cmd, sizeof(cmd));
+}
+
+/* 0x13 - Set port disconnect type
+ *	0: none
+ *	1: AC-disconnect
+ *	2: DC-disconnect
+ *	3: DC with delay
+ */
+static int
+poe_cmd_port_disconnect_type(unsigned char port, unsigned char type)
+{
+	unsigned char cmd[] = { 0x13, 0x00, port, type };
+
+	return poe_cmd_queue(cmd, sizeof(cmd));
+}
+
+/* 0x15 - Set port power limit type
+ *	0: None. Power limit is 16.2W if the connected device is “low power”,
+ *	   or the set high power limit if the device is “high power”.
+ *	1: Class based. The power limit for class 4 devices is determined by the high power limit.
+ *	2: User defined
+ */
+static int
+poe_cmd_port_power_limit_type(unsigned char port, unsigned char type)
+{
+	unsigned char cmd[] = { 0x15, 0x00, port, type };
+
+	return poe_cmd_queue(cmd, sizeof(cmd));
+}
+
+/* 0x16 - Set port power budget
+ *	values in 0.2W increments
+ */
+static int
+poe_cmd_port_power_budget(unsigned char port, unsigned char budget)
+{
+	unsigned char cmd[] = { 0x16, 0x00, port, budget };
+
+	return poe_cmd_queue(cmd, sizeof(cmd));
+}
+
+/* 0x17 - Set power management mode
+ *	0: None (No Power Management mode) (Default in Semi-Auto mode)
+ *	1: Static Power Management with Port Priority(Default in Automode)
+ *	2: Dynamic Power Management with Port Priority
+ *	3: Static Power Management without Port Priority
+ *	4: Dynamic Power Management without Port Priority
+ */
+static int
+poe_cmd_power_mgmt_mode(unsigned char mode)
+{
+	unsigned char cmd[] = { 0x18, 0x00, mode };
+
+	return poe_cmd_queue(cmd, sizeof(cmd));
+}
+
+/* 0x18 - Set global power budget */
+static int
+poe_cmd_global_power_budget(int budget, int guard)
+{
+	unsigned char cmd[] = { 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+	cmd[3] = budget * 10 / 256;
+	cmd[4] = budget * 10 % 256;
+	cmd[5] = guard * 10 / 256;
+	cmd[6] = guard * 10 % 256;
+
+	return poe_cmd_queue(cmd, sizeof(cmd));
+}
+
+/* 0x1a - Set port priority
+ *	0: Low
+ *	1: Normal
+ *	2: High
+ *	3: Critical
+ */
+static int
+poe_set_port_priority(unsigned char port, unsigned char priority)
+{
+	unsigned char cmd[] = { 0x1a, 0x00, port, priority };
+
+	return poe_cmd_queue(cmd, sizeof(cmd));
+}
+
+/* 0x1c - Set port power-up mode
+ *	0: PoE
+ *	1: legacy
+ *	2: pre-PoE+
+ *	3: PoE+
+ */
+static int
+poe_set_port_power_up_mode(unsigned char port, unsigned char mode)
+{
+	unsigned char cmd[] = { 0x1c, 0x00, port, mode };
+
+	return poe_cmd_queue(cmd, sizeof(cmd));
+}
+
+/* 0x20 - Get system info */
+static int
+poe_cmd_status(void)
+{
+	unsigned char cmd[] = { 0x20 };
+
+	return poe_cmd_queue(cmd, sizeof(cmd));
+}
+
+static int
+poe_reply_status(unsigned char *reply)
+{
+	static char *mode[]={
+		"Semi-auto I2C",
+		"Semi-auto UART",
+		"Auto I2C",
+		"Auto UART"
+	};
+	static char *mcu[]={
+		"ST Micro ST32F100 Microcontroller",
+		"Nuvoton M05xx LAN Microcontroller",
+		"ST Micro STF030C8 Microcontroller",
+		"Nuvoton M058SAN Microcontroller",
+		"Nuvoton NUC122 Microcontroller"
+	};
+	static char *status[]={
+		"Global Disable pin is de-asserted:No system reset from the previous query cmd:Configuration saved",
+		"Global Disable pin is de-asserted:No system reset from the previous query cmd:Configuration Dirty",
+		"Global Disable pin is de-asserted:System reseted:Configuration saved",
+		"Global Disable pin is de-asserted:System reseted:Configuration Dirty",
+		"Global Disable Pin is asserted:No system reset from the previous query cmd:Configuration saved",
+		"Global Disable Pin is asserted:No system reset from the previous query cmd:Configuration Dirty",
+		"Global Disable Pin is asserted:System reseted:Configuration saved",
+		"Global Disable Pin is asserted:System reseted:Configuration Dirty"
+	};
+
+	state.sys_mode = GET_STR(reply[2], mode);
+	config.port_count = reply[3];
+	state.sys_version = reply[7];
+	state.sys_mcu = GET_STR(reply[8], mcu);
+	state.sys_status = GET_STR(reply[9], status);
+	state.sys_ext_version = reply[10];
+
+	return 0;
+}
+
+/* 0x23 - Get power statistics */
+static int
+poe_cmd_power_stats(void)
+{
+	unsigned char cmd[] = { 0x23 };
+
+	return poe_cmd_queue(cmd, sizeof(cmd));
+}
+
+static int
+poe_reply_power_stats(unsigned char *reply)
+{
+	state.power_consumption = reply[2];
+	state.power_consumption *= 256;
+	state.power_consumption += reply[3];
+	state.power_consumption /= 10;
+
+	return 0;
+}
+
+/* 0x26 - Get extended port config */
+static int
+poe_cmd_port_ext_config(unsigned char port)
+{
+	unsigned char cmd[] = { 0x26, 0x00, port };
+
+	return poe_cmd_queue(cmd, sizeof(cmd));
+}
+
+static int
+poe_reply_port_ext_config(unsigned char *reply)
+{
+	static char *mode[] = {
+		"PoE",
+		"Legacy",
+		"pre-PoE+",
+		"PoE+"
+	};
+
+	state.ports[reply[2]].poe_mode = GET_STR(reply[3], mode);
+
+	return 0;
+}
+
+/* 0x2a - Get all port status */
+static int
+poe_cmd_port_overview(void)
+{
+	unsigned char cmd[] = { 0x2a, 0x00, 0x00 };
+
+	return poe_cmd_queue(cmd, sizeof(cmd));
+}
+
+static int
+poe_reply_port_overview(unsigned char *reply)
+{
+	static char *status[]={
+		"Disabled",
+		"Searching",
+		"Delivering power",
+		"Fault",
+		"Other fault",
+		"Requesting power",
+	};
+	int i;
+
+	for (i = 0; i < 8; i++)
+		state.ports[i].status = GET_STR((reply[3 + i] & 0xf), status);
+
+	return 0;
+}
+
+/* 0x30 - Get port power statistics */
+static int
+poe_cmd_port_power_stats(unsigned char port)
+{
+	unsigned char cmd[] = { 0x30, 0x00, port };
+
+	return poe_cmd_queue(cmd, sizeof(cmd));
+}
+
+static int
+poe_reply_port_power_stats(unsigned char *reply)
+{
+	float watt;
+
+	watt = reply[9];
+	watt *= 256;
+	watt += reply[10];
+	watt /= 10;
+
+	state.ports[reply[2]].watt = watt;
+
+	return 0;
+}
+
+static poe_reply_handler reply_handler[] = {
+	[0x20] = poe_reply_status,
+	[0x23] = poe_reply_power_stats,
+	[0x26] = poe_reply_port_ext_config,
+	[0x2a] = poe_reply_port_overview,
+	[0x30] = poe_reply_port_power_stats,
+};
+
+static int
+poe_reply_consume(unsigned char *reply)
+{
+	struct cmd *cmd = NULL;
+	unsigned char sum = 0, i;
+
+	poe_cmd_dump("RX", reply);
+
+	if (list_empty(&cmd_pending)) {
+		ULOG_ERR("received unsolicited reply\n");
+		return -1;
+	}
+
+	cmd = list_first_entry(&cmd_pending, struct cmd, list);
+	list_del(&cmd->list);
+
+	for (i = 0; i < 11; i++)
+		sum += reply[i];
+
+	if (reply[11] != sum) {
+		ULOG_DBG("received reply with bad checksum\n");
+		return -1;
+	}
+
+	if (reply[0] != cmd->cmd[0]) {
+		ULOG_DBG("received reply with bad command id\n");
+		return -1;
+	}
+
+	if (reply[1] != cmd->cmd[1]) {
+		ULOG_DBG("received reply with bad sequence number\n");
+		return -1;
+	}
+
+	free(cmd);
+
+	if (reply_handler[reply[0]])
+		return reply_handler[reply[0]](reply);
+
+	return 0;
+}
+
+static void
+poe_stream_msg_cb(struct ustream *s, int bytes)
+{
+	int len;
+	unsigned char *reply = (unsigned char *)ustream_get_read_buf(s, &len);
+
+	if (len < 12)
+		return;
+
+	poe_reply_consume(reply);
+	ustream_consume(s, 12);
+	poe_cmd_next();
+}
+
+static void
+poe_stream_notify_cb(struct ustream *s)
+{
+	if (!s->eof)
+		return;
+
+	ULOG_ERR("tty error, shutting down\n");
+	exit(-1);
+}
+
+static int
+poe_stream_open(char *dev, struct ustream_fd *s, speed_t speed)
+{
+	struct termios tio;
+	int tty;
+
+	tty = open(dev, O_RDWR | O_NOCTTY | O_NONBLOCK);
+	if (tty < 0) {
+		ULOG_ERR("%s: device open failed: %s\n", dev, strerror(errno));
+		return -1;
+	}
+
+	tcgetattr(tty, &tio);
+	tio.c_cflag |= CREAD;
+	tio.c_cflag |= CS8;
+	tio.c_iflag |= IGNPAR;
+	tio.c_lflag &= ~(ICANON);
+	tio.c_lflag &= ~(ECHO);
+	tio.c_lflag &= ~(ECHOE);
+	tio.c_lflag &= ~(ISIG);
+	tio.c_iflag &= ~(IXON | IXOFF | IXANY);
+	tio.c_cflag &= ~CRTSCTS;
+	tio.c_cc[VMIN] = 1;
+	tio.c_cc[VTIME] = 0;
+	cfsetispeed(&tio, speed);
+	cfsetospeed(&tio, speed);
+	tcsetattr(tty, TCSANOW, &tio);
+
+	s->stream.string_data = false;
+	s->stream.notify_read = poe_stream_msg_cb;
+	s->stream.notify_state = poe_stream_notify_cb;
+
+	ustream_fd_init(s, tty);
+	tcflush(tty, TCIFLUSH);
+
+	return 0;
+}
+
+static int
+poe_port_setup(void)
+{
+	int i;
+
+	for (i = 0; i < config.port_count; i++) {
+		if (!config.ports[i].enable) {
+			poe_cmd_port_enable(i, 0);
+			continue;
+		}
+
+		poe_set_port_priority(i, config.ports[i].priority);
+		poe_set_port_power_up_mode(i, config.ports[i].power_up_mode);
+		if (config.ports[i].power_budget) {
+			poe_cmd_port_power_budget(i, config.ports[i].power_budget);
+			poe_cmd_port_power_limit_type(i, 2);
+		} else {
+			poe_cmd_port_power_limit_type(i, 1);
+		}
+		poe_cmd_port_disconnect_type(i, 2);
+		poe_cmd_port_classification(i, 1);
+		poe_cmd_port_detection_type(i, 3);
+		poe_cmd_port_enable(i, 1);
+	}
+
+	return 0;
+}
+
+static int
+poe_initial_setup(void)
+{
+	poe_cmd_status();
+	poe_cmd_power_mgmt_mode(2);
+	poe_cmd_global_power_budget(0, 0);
+	poe_cmd_global_port_enable(0);
+	poe_cmd_global_power_budget(config.budget, config.budget_guard);
+
+	poe_port_setup();
+
+	return 0;
+}
+
+static void
+state_timeout_cb(struct uloop_timeout *t)
+{
+	int i;
+
+	poe_cmd_power_stats();
+	poe_cmd_port_overview();
+
+	for (i = 0; i < config.port_count; i++) {
+		poe_cmd_port_ext_config(i);
+		poe_cmd_port_power_stats(i);
+	}
+
+	uloop_timeout_set(&state_timeout, 2 * 1000);
+}
+
+static int
+ubus_poe_info_cb(struct ubus_context *ctx, struct ubus_object *obj,
+		 struct ubus_request_data *req, const char *method,
+		 struct blob_attr *msg)
+{
+	char tmp[16];
+	void *c;
+	int i;
+
+	blob_buf_init(&b, 0);
+
+	snprintf(tmp, sizeof(tmp), "v%u.%u",
+		 state.sys_version, state.sys_ext_version);
+	blobmsg_add_string(&b, "firmware", tmp);
+	if (state.sys_mcu)
+		blobmsg_add_string(&b, "mcu", state.sys_mcu);
+	blobmsg_add_double(&b, "budget", config.budget);
+	blobmsg_add_double(&b, "consumption", state.power_consumption);
+
+	c = blobmsg_open_table(&b, "ports");
+	for (i = 0; i < config.port_count; i++) {
+		void *p;
+
+		if (!config.ports[i].enable)
+			continue;
+
+		p = blobmsg_open_table(&b, config.ports[i].name);
+
+		blobmsg_add_u32(&b, "priority", config.ports[i].priority);
+
+		if (state.ports[i].poe_mode)
+			blobmsg_add_string(&b, "mode", state.ports[i].poe_mode);
+		if (state.ports[i].status)
+			blobmsg_add_string(&b, "status", state.ports[i].status);
+		else
+			blobmsg_add_string(&b, "status", "unknown");
+		if (state.ports[i].watt)
+			blobmsg_add_double(&b, "consumption", state.ports[i].watt);
+
+		blobmsg_close_table(&b, p);
+	}
+	blobmsg_close_table(&b, c);
+
+	ubus_send_reply(ctx, req, b.head);
+
+	return UBUS_STATUS_OK;
+}
+
+static int
+ubus_poe_reload_cb(struct ubus_context *ctx, struct ubus_object *obj,
+		   struct ubus_request_data *req, const char *method,
+		   struct blob_attr *msg)
+{
+	config_load(0);
+	poe_port_setup();
+
+	return UBUS_STATUS_OK;
+}
+
+static const struct ubus_method ubus_poe_methods[] = {
+	UBUS_METHOD_NOARG("info", ubus_poe_info_cb),
+	UBUS_METHOD_NOARG("reload", ubus_poe_reload_cb),
+};
+
+static struct ubus_object_type ubus_poe_object_type =
+	UBUS_OBJECT_TYPE("poe", ubus_poe_methods);
+
+static struct ubus_object ubus_poe_object = {
+	.name = "poe",
+	.type = &ubus_poe_object_type,
+	.methods = ubus_poe_methods,
+	.n_methods = ARRAY_SIZE(ubus_poe_methods),
+};
+
+static void
+ubus_connect_handler(struct ubus_context *ctx)
+{
+	int ret;
+
+	ret = ubus_add_object(ctx, &ubus_poe_object);
+	if (ret)
+		fprintf(stderr, "Failed to add object: %s\n", ubus_strerror(ret));
+}
+
+int
+main(int argc, char ** argv)
+{
+	int ch;
+
+	ulog_open(ULOG_STDIO | ULOG_SYSLOG, LOG_DAEMON, "realtek-poe");
+	ulog_threshold(LOG_INFO);
+
+	while ((ch = getopt(argc, argv, "d")) != -1) {
+		switch (ch) {
+		case 'd':
+			config.debug = 1;
+			break;
+		}
+	}
+
+	config_load(1);
+
+	uloop_init();
+	conn.cb = ubus_connect_handler;
+	ubus_auto_connect(&conn);
+
+	if (poe_stream_open("/dev/ttyS1", &stream, B19200) < 0)
+		return -1;
+
+	poe_initial_setup();
+	state_timeout.cb = state_timeout_cb;
+	uloop_timeout_set(&state_timeout, 1000);
+	uloop_run();
+	uloop_done();
+
+	return 0;
+}