Add support for sending and handling supervision frames. For PRP,
supervision frame format is similar to HSR version 0, but have a
PRP Redunancy Control Trailor (RCT) added.

Signed-off-by: Murali Karicheri <[email protected]>
---
 net/hsr-prp/hsr_prp_device.c  | 50 ++++++++++++++++++++++++++++-------
 net/hsr-prp/hsr_prp_forward.c |  4 ++-
 net/hsr-prp/hsr_prp_main.h    | 22 +++++++++++++++
 3 files changed, 65 insertions(+), 11 deletions(-)

diff --git a/net/hsr-prp/hsr_prp_device.c b/net/hsr-prp/hsr_prp_device.c
index 501de23a97f5..3c463e185f64 100644
--- a/net/hsr-prp/hsr_prp_device.c
+++ b/net/hsr-prp/hsr_prp_device.c
@@ -237,13 +237,20 @@ static void send_hsr_prp_supervision_frame(struct 
hsr_prp_port *master,
 {
        struct sk_buff *skb;
        int hlen, tlen;
-       struct hsr_tag *hsr_tag;
+       struct hsr_tag *hsr_tag = NULL;
+       struct prp_rct *rct;
        struct hsr_prp_sup_tag *hsr_stag;
        struct hsr_prp_sup_payload *hsr_sp;
        unsigned long irqflags;
+       u16 proto;
+       u8 *tail;
 
        hlen = LL_RESERVED_SPACE(master->dev);
        tlen = master->dev->needed_tailroom;
+       /* skb size is same for PRP/HSR frames, only difference
+        * being for PRP, it is a trailor and for HSR it is a
+        * header
+        */
        skb = dev_alloc_skb(sizeof(struct hsr_tag) +
                            sizeof(struct hsr_prp_sup_tag) +
                            sizeof(struct hsr_prp_sup_payload) + hlen + tlen);
@@ -252,12 +259,15 @@ static void send_hsr_prp_supervision_frame(struct 
hsr_prp_port *master,
                return;
 
        skb_reserve(skb, hlen);
-
+       if (!proto_ver)
+               proto = ETH_P_PRP;
+       else
+               proto = (proto_ver == HSR_V1) ? ETH_P_HSR : ETH_P_PRP;
        skb->dev = master->dev;
-       skb->protocol = htons(proto_ver ? ETH_P_HSR : ETH_P_PRP);
+       skb->protocol = htons(proto);
        skb->priority = TC_PRIO_CONTROL;
 
-       if (dev_hard_header(skb, skb->dev, (proto_ver ? ETH_P_HSR : ETH_P_PRP),
+       if (dev_hard_header(skb, skb->dev, proto,
                            master->priv->sup_multicast_addr,
                            skb->dev->dev_addr, skb->len) <= 0)
                goto out;
@@ -265,7 +275,7 @@ static void send_hsr_prp_supervision_frame(struct 
hsr_prp_port *master,
        skb_reset_network_header(skb);
        skb_reset_transport_header(skb);
 
-       if (proto_ver > 0) {
+       if (proto_ver == HSR_V1) {
                hsr_tag = skb_put(skb, sizeof(struct hsr_tag));
                hsr_tag->encap_proto = htons(ETH_P_PRP);
                set_hsr_tag_LSDU_size(hsr_tag, HSR_V1_SUP_LSDUSIZE);
@@ -273,15 +283,19 @@ static void send_hsr_prp_supervision_frame(struct 
hsr_prp_port *master,
 
        hsr_stag = skb_put(skb, sizeof(struct hsr_prp_sup_tag));
        set_hsr_stag_path(hsr_stag, (proto_ver ? 0x0 : 0xf));
-       set_hsr_stag_HSR_ver(hsr_stag, proto_ver);
+       set_hsr_stag_HSR_ver(hsr_stag, proto_ver ? 0x1 : 0x0);
 
        /* From HSRv1 on we have separate supervision sequence numbers. */
        spin_lock_irqsave(&master->priv->seqnr_lock, irqflags);
        if (proto_ver > 0) {
                hsr_stag->sequence_nr = htons(master->priv->sup_sequence_nr);
-               hsr_tag->sequence_nr = htons(master->priv->sequence_nr);
+               if (hsr_tag)
+                       hsr_tag->sequence_nr = htons(master->priv->sequence_nr);
                master->priv->sup_sequence_nr++;
-               master->priv->sequence_nr++;
+               if (proto_ver == HSR_V1) {
+                       hsr_tag->sequence_nr = htons(master->priv->sequence_nr);
+                       master->priv->sequence_nr++;
+               }
        } else {
                hsr_stag->sequence_nr = htons(master->priv->sequence_nr);
                master->priv->sequence_nr++;
@@ -300,6 +314,16 @@ static void send_hsr_prp_supervision_frame(struct 
hsr_prp_port *master,
        if (skb_put_padto(skb, ETH_ZLEN + HSR_PRP_HLEN))
                return;
 
+       spin_lock_irqsave(&master->priv->seqnr_lock, irqflags);
+       if (proto_ver == PRP_V1) {
+               tail = skb_tail_pointer(skb) - HSR_PRP_HLEN;
+               rct = (struct prp_rct *)tail;
+               rct->PRP_suffix = htons(ETH_P_PRP);
+               set_prp_LSDU_size(rct, HSR_V1_SUP_LSDUSIZE);
+               rct->sequence_nr = htons(master->priv->sequence_nr);
+               master->priv->sequence_nr++;
+       }
+       spin_unlock_irqrestore(&master->priv->seqnr_lock, irqflags);
        hsr_prp_forward_skb(skb, master);
        return;
 
@@ -328,8 +352,14 @@ static void hsr_prp_announce(struct timer_list *t)
 
                interval = msecs_to_jiffies(HSR_PRP_ANNOUNCE_INTERVAL);
        } else {
-               send_hsr_prp_supervision_frame(master, HSR_TLV_LIFE_CHECK,
-                                              priv->prot_version);
+               if (priv->prot_version <= HSR_V1)
+                       send_hsr_prp_supervision_frame(master,
+                                                      HSR_TLV_LIFE_CHECK,
+                                                      priv->prot_version);
+               else /* PRP */
+                       send_hsr_prp_supervision_frame(master,
+                                                      PRP_TLV_LIFE_CHECK_DD,
+                                                      priv->prot_version);
 
                interval = msecs_to_jiffies(HSR_PRP_LIFE_CHECK_INTERVAL);
        }
diff --git a/net/hsr-prp/hsr_prp_forward.c b/net/hsr-prp/hsr_prp_forward.c
index 59b33d711ea6..d7e975919322 100644
--- a/net/hsr-prp/hsr_prp_forward.c
+++ b/net/hsr-prp/hsr_prp_forward.c
@@ -74,7 +74,9 @@ static bool is_supervision_frame(struct hsr_prp_priv *priv, 
struct sk_buff *skb)
        }
 
        if (hsr_sup_tag->HSR_TLV_type != HSR_TLV_ANNOUNCE &&
-           hsr_sup_tag->HSR_TLV_type != HSR_TLV_LIFE_CHECK)
+           hsr_sup_tag->HSR_TLV_type != HSR_TLV_LIFE_CHECK &&
+           hsr_sup_tag->HSR_TLV_type != PRP_TLV_LIFE_CHECK_DD &&
+           hsr_sup_tag->HSR_TLV_type != PRP_TLV_LIFE_CHECK_DA)
                return false;
        if (hsr_sup_tag->HSR_TLV_length != 12 &&
            hsr_sup_tag->HSR_TLV_length != sizeof(struct hsr_prp_sup_payload))
diff --git a/net/hsr-prp/hsr_prp_main.h b/net/hsr-prp/hsr_prp_main.h
index 00c312e5189f..17049d040226 100644
--- a/net/hsr-prp/hsr_prp_main.h
+++ b/net/hsr-prp/hsr_prp_main.h
@@ -33,6 +33,10 @@
 
 #define HSR_TLV_ANNOUNCE                  22
 #define HSR_TLV_LIFE_CHECK                23
+/* PRP V1 life check for Duplicate discard */
+#define PRP_TLV_LIFE_CHECK_DD             20
+/* PRP V1 life check for Duplicate Accept */
+#define PRP_TLV_LIFE_CHECK_DA             21
 
 /* HSR Tag.
  * As defined in IEC-62439-3:2010, the HSR tag is really { ethertype = 0x88FB,
@@ -125,6 +129,24 @@ enum hsr_prp_port_type {
        HSR_PRP_PT_PORTS,       /* This must be the last item in the enum */
 };
 
+/* PRP Redunancy Control Trailor (RCT).
+ * As defined in IEC-62439-4:2012, the PRP RCT is really { sequence Nr,
+ * Lan indentifier (LanId), LSDU_size and PRP_suffix = 0x88FB }.
+ *
+ * Field names as defined in the IEC:2012 standard for PRP.
+ */
+struct prp_rct {
+       __be16          sequence_nr;
+       __be16          lan_id_and_LSDU_size;
+       __be16          PRP_suffix;
+} __packed;
+
+static inline void set_prp_LSDU_size(struct prp_rct *rct, u16 LSDU_size)
+{
+       rct->lan_id_and_LSDU_size = htons((ntohs(rct->lan_id_and_LSDU_size) &
+                                         0xF000) | (LSDU_size & 0x0FFF));
+}
+
 struct hsr_prp_port {
        struct list_head        port_list;
        struct net_device       *dev;
-- 
2.17.1

Reply via email to