Support for RTL838x based managed switches

TL;DR

  1. I want realtek-poe to work, but to do so we need an agreed-upon place for it to be developed
  2. I don't think realtek-poe will make it to mainline OpenWrt, but as it stands there's no OpenWrt-based implementation that can successfully control this very commonly seen MCU+BCM59XXX-backed PoE hardware, and I think realtek-poe is useful as an open-source reference implementation for debugging.

On my GS1900-24HPv1, the issue I'm replying to was caused by MAX_PORTS being hard-set as 8 in realtek-poe's main.c. After changing that (and another instance which hard-codes 8 in a for loop in poe_reply_port_overview), the program runs, but does not successfully initialize PoE on any ports (so far that I've tested). I think this is because of a port mapping issue which must be dealt with for these 24-port switches.

Two things in particular:

  1. The 0x0ath sent frame always receives a response 0xfe, AKA 'Request Checksum Frame was incorrect'. I've tested multiple different configurations and found that no matter what the frame is, as long as it's the 0x0ath one, it will fail. I don't believe this is true when the Frame ID wraps over to 0x0a again.

  2. Frames sent to manipulate configuration on port 0x0a always fail with response 0xfe -- and frames to manipulate port 0x0d respond with information from port 0x0a. I suspect this has to do with port mapping, and that we will have to find the original firmware's port mapping and reproduce it.

I have implemented a ubus callback to queue up one frame:

root@OpenWrt:/# ubus -v list poe
'poe' @38a3bba9
        "info":{}
        "reload":{}
        "sendframe":{"frame":"String"}

Using (roughly) this patch:

@ -773,6 +866,47 @@ ubus_poe_info_cb(struct ubus_context *ctx, struct ubus_object *obj,
     return UBUS_STATUS_OK;
 }

+enum {
+    POE_SENDFRAME_STRING,
+    __POE_SENDFRAME_MAX
+};
+
+static const struct blobmsg_policy ubus_poe_sendframe_policy[__POE_SENDFRAME_MAX] = {
+    [POE_SENDFRAME_STRING] = { "frame", BLOBMSG_TYPE_STRING },
+};
+
+static int
+ubus_poe_sendframe_cb(struct ubus_context *ctx, struct ubus_object *obj,
+           struct ubus_request_data *req, const char *method,
+           struct blob_attr *msg)
+{
+    struct blob_attr *tb[__POE_SENDFRAME_MAX];
+    blobmsg_parse(ubus_poe_sendframe_policy, __POE_SENDFRAME_MAX, tb, blob_data(msg), blob_len(msg));
+
+    if (!tb[POE_SENDFRAME_STRING])
+        return UBUS_STATUS_INVALID_ARGUMENT;
+
+    char *frame = blobmsg_get_string(tb[POE_SENDFRAME_STRING]);
+
+    unsigned int frame_strlen = strlen(frame);
+    unsigned char cmd[9];
+    unsigned int cmdsize = 0;
+    unsigned char *end = cmd + sizeof(cmd);
+    unsigned int u;
+    unsigned int i = 0;
+
+    while ((cmd + cmdsize < end) && (i < frame_strlen)) {
+        if (sscanf((frame+i), "0x%2x", &u) == 1) {
+            cmd[cmdsize++] = u;
+            i += 4;
+        } else
+            i += 1;
+    }
+    return poe_cmd_queue(cmd, cmdsize);
+}
+
 static int
 ubus_poe_reload_cb(struct ubus_context *ctx, struct ubus_object *obj,
            struct ubus_request_data *req, const char *method,
@@ -787,6 +921,7 @@ ubus_poe_reload_cb(struct ubus_context *ctx, struct ubus_object *obj,
 static const struct ubus_method ubus_poe_methods[] = {
     UBUS_METHOD_NOARG("info", ubus_poe_info_cb),
     UBUS_METHOD_NOARG("reload", ubus_poe_reload_cb),
+    UBUS_METHOD("sendframe", ubus_poe_sendframe_cb, ubus_poe_sendframe_policy),
 };

This will enqueue up to 9 bytes as a frame (using the same code path as all of the frame command senders to handle frame number / checksumming); unfortunately I'm not a strong C programmer and haven't thought of a good way to retrieve the response to the frame as the frame is only enqueued and not waited on -- but I might be misunderstanding the running context of the ubus_poe_sendframe_cb when one calls ubus call poe sendframe '{"frame": "0x01 0x01"}' (to enable port 1, for example); it might be possible to have the method here spin / wait for the frame response, I'm just not really familiar with the ubus architecture.

To be clear, the code does work and does send the frame you ask; if running realtek-poe -d you do see the frame being sent upon running that ubus call.

It'd be nice to have a place to send my patches, or even an official home for realtek-poe to be developed, but ca n'existe pas at this point. I'm happy to create a repository / specific branch for code development on Github if that would help centralize efforts, @blogic.

4 Likes

I can spread this realtek-poe-based stuff to another thread.

3 Likes

Wait, the switch is doing double tagging? That seems odd and unnecessary, at least to me. I wonder why?

Guessing the internal double tags are necessary to be able to support q-in-q. Or alternatively more than 1 bridge group on the same switch with overlapping VLANs.

The RTL switch hardware supports an Outer and an Inner Tag on any packet in order to support Q-in-Q. A switching decision is only based on one of these tags at a point in time in the switch, never on both simultaneously.

i have used dgs-1210-28P F2 openwrt firmware for 14 months with 5 clients, only default vlan 1 on all ports . There were not any problem. Thanks to developers..

1 Like

I've created pages at Linux support for Realtek switches and WikiDevi for the ZyXEL GS1900-24EP with details I have been able to gather so far. I've also requested the GPL source code from ZyXEL. I'm hoping it will provide additional information regarding the RTL8238 PSE Controllers. I'm guessing they are 8 port controllers with only 4 ports used on one for the 12 PoE ports on this switch. I was unable to remove the heat sinks, so hoping someone can tell me how to identify the model of the 2 8-port PHY extenders used.

1 Like

One way to identify PHYs is to look a the results of the mdio bus being probed in order to identify all PHYs. This is done automatically by the DSA subsystem. To see the result, you need to add the following into rtl838x.c:rtl838x_read_phy() in a new line before the "return 0;" statement:

	pr_info("%s: port %d, page: %d, reg: %x, val: %x\n",
		__func__, port, page, reg, *val);

The PHY registers 2 and 3 in page 0 then define the PHY-ID. The PHYs so far encountered can be seen in rtl83xx-phy.h . My bet would be on 2 external RTL8218B.