Implement NMReset in case a node goes missing
authornorly <ny-git@enpas.org>
Sat, 18 Mar 2017 23:23:49 +0000 (00:23 +0100)
committernorly <ny-git@enpas.org>
Sat, 18 Mar 2017 23:24:05 +0000 (00:24 +0100)
vw-nm-tools.h
vw-nm.c
vw-nm.h

index 426e778848f8894d3c94303b8d03675dc0b57f0a..f4126a69f06e3c99eb19409b153841567ddd127f 100644 (file)
@@ -76,6 +76,81 @@ static void can_tx(int socket, struct can_frame *frame)
 
 
 
+static int nm_is_rx_frame_valid(struct NM_Main *nm, struct can_frame *frame)
+{
+       if (frame->can_dlc < 2) {
+               printf("Skipping short frame from CAN ID %03x\n", frame->can_id);
+               return 0;
+       }
+
+       if ((frame->can_id & ~(nm->max_nodes - 1)) != nm->can_base) {
+               printf("Skipping non-NM from CAN ID %03x\n", frame->can_id);
+               return 0;
+       }
+
+       return 1;
+}
+
+
+
+
+
+
+
+
+
+
+static void nm_set_timer_now(struct NM_Main *nm)
+{
+       nm->tv.tv_sec = 0;
+       nm->tv.tv_usec = 0;
+       nm->timer_reason = NM_TIMER_NOW;
+}
+
+static void nm_set_timer_normal(struct NM_Main *nm)
+{
+       nm->tv.tv_sec = 0;
+       nm->tv.tv_usec = NM_USECS_NORMAL_TURN;
+       nm->timer_reason = NM_TIMER_NORMAL;
+}
+
+static void nm_set_timer_awol(struct NM_Main *nm)
+{
+       nm->tv.tv_sec = 0;
+       nm->tv.tv_usec = NM_USECS_NODE_AWOL;
+       nm->timer_reason = NM_TIMER_AWOL;
+}
+
+/*
+static void nm_set_timer_limphome(struct NM_Main *nm)
+{
+       nm->tv.tv_sec = 0;
+       nm->tv.tv_usec = NM_USECS_LIMPHOME;
+       nm->timer_reason = NM_TIMER_LIMPHOME;
+}
+*/
+
+
+
+
+
+
+static void nm_reset(struct NM_Main *nm)
+{
+       unsigned id;
+
+       for (id = 0; id < nm->max_nodes; id++) {
+               nm->nodes[id].next = 0xff;
+               nm->nodes[id].state = NM_MAIN_OFF;
+       }
+
+       nm->nodes[nm->my_id].next = nm->my_id;
+       nm->nodes[nm->my_id].state = NM_MAIN_LOGIN;
+
+       nm_set_timer_normal(nm);
+}
+
+
 
 static struct NM_Main* nm_alloc(unsigned node_bits, NM_ID my_id, canid_t can_base)
 {
@@ -100,11 +175,7 @@ static struct NM_Main* nm_alloc(unsigned node_bits, NM_ID my_id, canid_t can_bas
        nm->my_id = my_id;
        nm->can_base = can_base;
 
-       nm->nodes[nm->my_id].next = nm->my_id;
-       nm->nodes[nm->my_id].state = NM_MAIN_LOGIN;
-
-       nm->tv.tv_sec = 0;
-       nm->tv.tv_usec = NM_USECS_NODE_AWOL;
+       nm_reset(nm);
 
        return nm;
 }
diff --git a/vw-nm.c b/vw-nm.c
index 6a3ca05bb311b1aeaff6f02159f055cced754a8b..c8439026fbef7ba03ec1aab073f012bfd7e309e7 100644 (file)
--- a/vw-nm.c
+++ b/vw-nm.c
 
 
 
-static int nm_is_rx_frame_valid(struct NM_Main *nm, struct can_frame *frame)
-{
-       if (frame->can_dlc < 2) {
-               printf("Skipping short frame from CAN ID %03x\n", frame->can_id);
-               return 0;
-       }
-
-       if ((frame->can_id & ~(nm->max_nodes - 1)) != nm->can_base) {
-               printf("Skipping non-NM from CAN ID %03x\n", frame->can_id);
-               return 0;
-       }
-
-       return 1;
-}
-
-
-
-static void nm_set_timer_now(struct NM_Main *nm) {
-       nm->tv.tv_sec = 0;
-       nm->tv.tv_usec = 0;
-       nm->timer_reason = NM_TIMER_NOW;
-}
-
-static void nm_set_timer_normal(struct NM_Main *nm) {
-       nm->tv.tv_sec = 0;
-       nm->tv.tv_usec = NM_USECS_NORMAL_TURN;
-       nm->timer_reason = NM_TIMER_NORMAL;
-}
-
-static void nm_set_timer_awol(struct NM_Main *nm) {
-       nm->tv.tv_sec = 0;
-       nm->tv.tv_usec = NM_USECS_NODE_AWOL;
-       nm->timer_reason = NM_TIMER_AWOL;
-}
-
-/*
-static void nm_set_timer_limphome(struct NM_Main *nm) {
-       nm->tv.tv_sec = 0;
-       nm->tv.tv_usec = NM_USECS_LIMPHOME;
-       nm->timer_reason = NM_TIMER_LIMPHOME;
-}
-*/
 
 
 static void nm_update_my_next_id(struct NM_Main *nm) {
@@ -132,11 +90,23 @@ static void nm_handle_can_frame(struct NM_Main *nm, struct can_frame *frame)
        nm->nodes[sender].next = next;
        nm->nodes[sender].state = state;
 
+       /* Update our view of the world */
+       nm_update_my_next_id(nm);
+
        switch (state & NM_MAIN_MASK) {
                case NM_MAIN_ON:
+                       /* We're not alone, so let's transition to ON for now.
+                        */
+                       nm->nodes[nm->my_id].state = NM_MAIN_ON;
+
+                       /* The AWOL timeout is ONLY reset on
+                        * NM_MAIN_ON messages.
+                        */
+                       nm_set_timer_awol(nm);
+
                        if (next == nm->nodes[nm->my_id].next
                                && nm->nodes[nm->my_id].next != nm->my_id) {
-                               /* sender doesn't know we exist */
+                               /* Sender doesn't know we exist */
 
                                nm->nodes[nm->my_id].state = NM_MAIN_LOGIN;
 
@@ -157,49 +127,35 @@ static void nm_handle_can_frame(struct NM_Main *nm, struct can_frame *frame)
                                 *    NM frame.
                                 */
 
-                               /* Let's handle this just like a LOGIN, since
-                                * we're learning about a new device.
-                                * See case NM_MAIN_LOGIN below for details.
-                                */
-
-                               nm_update_my_next_id(nm);
-                               nm->nodes[nm->my_id].state = NM_MAIN_ON;
+                               /* Nothing to do. */
                        } else if (next == nm->my_id) {
-                               /* It's our turn.
-                                * Reset the timeout so anyone we missed
-                                * can send its login frame to correct us.
+                               /* It's our turn, do a normal timeout.
+                                * This is a period in which anyone we missed
+                                * can send its re-login frame to correct us.
                                 */
+
                                nm_set_timer_normal(nm);
                        } else {
-                               /* We just got some random ON message.
-                                * Reset the timer looking out for broken
-                                * connectivity.
-                                */
-                               nm_set_timer_awol(nm);
+                               /* We just received a random ON message. */
+
+                               /* Nothing to do. */
                        }
                        break;
                case NM_MAIN_LOGIN:
                        /* Note: sender != nm->my_id */
 
-                       nm_update_my_next_id(nm);
-
                        /* We're not alone anymore, so let's change state. */
                        nm->nodes[nm->my_id].state = NM_MAIN_ON;
 
-                       /* We don't reset the timeout when somebody logs in,
-                        * i.e. (next == sender).
+                       /* We don't reset the timeout when somebody logs in.
                         * Instead, we'll simply include them in the next
-                        * round. */
-
-                       /* Actually, when a login is done as a correction,
-                        * we do reset the timeout.
+                        * round.
                         */
-                       if (next != sender) {
-                               nm_set_timer_awol(nm);
-                       }
+
+                       /* Nothing else to do. */
                        break;
                case NM_MAIN_LIMPHOME:
-                       nm_update_my_next_id(nm);
+                       break;
        }
 
        nm_dump_all(nm);
@@ -223,23 +179,37 @@ static void nm_buildframe(struct NM_Main *nm, struct can_frame *frame)
 
 static void nm_timeout_callback(struct NM_Main *nm, struct can_frame *frame)
 {
-       nm_set_timer_awol(nm);
-
-       nm_buildframe(nm, frame);
-}
-
-
-
-
-static void nm_start(struct NM_Main *nm, struct can_frame *frame)
-{
-       nm->tv.tv_sec = 0;
-       nm->tv.tv_usec = NM_USECS_NODE_AWOL;
-
-
-
-       nm->nodes[nm->my_id].next = nm->my_id;
-       nm->nodes[nm->my_id].state = NM_MAIN_LOGIN;
+       switch(nm->timer_reason) {
+               case NM_TIMER_NOW:
+               case NM_TIMER_NORMAL:
+                       /* We're due to send our own ring message */
+                       switch(nm->nodes[nm->my_id].state & NM_MAIN_MASK) {
+                               case NM_MAIN_ON:
+                                       break;
+                               case NM_MAIN_LOGIN:
+                                       /* We're going to be ready, let's
+                                        * change state (RCD 310 behavior)
+                                        */
+                                       nm->nodes[nm->my_id].state = NM_MAIN_ON;
+                                       break;
+                               default:
+                                       printf("BUG: TIMER_NORMAL expired in unknown NM_MAIN state\n");
+                                       break;
+                       }
+                       nm_set_timer_awol(nm);
+                       nm_buildframe(nm, frame);
+                       break;
+               case NM_TIMER_AWOL:
+                       /* The network is silent because a node disappeared
+                        * or something bad happened.
+                        * Reset everything and start over.
+                        */
+                       nm_reset(nm);
+                       break;
+               case NM_TIMER_LIMPHOME:
+                       /* TODO */
+                       break;
+       }
 
        nm_buildframe(nm, frame);
 }
@@ -318,7 +288,7 @@ int main(int argc, char **argv)
        if (1) {
                struct can_frame frame;
 
-               nm_start(nm, &frame);
+               nm_buildframe(nm, &frame);
                can_tx(s, &frame);
        }
 
diff --git a/vw-nm.h b/vw-nm.h
index 2c38e72af82ff3101d7a1ccc5f0e62f28057bb9c..fcb6b4ba455ab8f5047ee4d982c95ad5cf3286a9 100644 (file)
--- a/vw-nm.h
+++ b/vw-nm.h
@@ -38,6 +38,7 @@ enum timer_reason {
 struct NM_Main {
        unsigned max_nodes;
        struct NM_Node *nodes;
+
        NM_ID my_id;
        canid_t can_base;
 
@@ -48,7 +49,9 @@ struct NM_Main {
 
 
 
-/* This timeout is ~49 ms in:
+/* OSEK/VDX NM: T_Typ
+ *
+ * This timeout is ~49 ms in:
  *  - 0x19 (RCD 310, Bosch)
  *    (sometimes it takes a little longer)
  *
@@ -62,13 +65,17 @@ struct NM_Main {
 #define NM_USECS_NORMAL_TURN 45000
 
 
-/* This timeout is 140 ms in:
+/* OSEK/VDX NM: T_Max
+ *
+ * This timeout is 140 ms in:
  *  - 0x19 (RCD 310, Bosch)
  */
 #define NM_USECS_NODE_AWOL 140000
 
 
-/* This timeout is 500 ms in:
+/* OSEK/VDX NM: T_Error
+ *
+ * This timeout is 500 ms in:
  *  - 0x19 (RCD 310, Bosch)
  */
 #define NM_USECS_LIMPHOME 500000