Timers: Simplify reset and reset out of limp home
[revag-nm.git] / vw-nm.c
diff --git a/vw-nm.c b/vw-nm.c
index 689cf694db56450e12bd639c60d77b4636bb1d0c..a181fce00866871671c1bd9f3dca7cdac0c15b08 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_update_my_next_id(struct NM_Main *nm) {
@@ -60,7 +44,7 @@ static void nm_update_my_next_id(struct NM_Main *nm) {
                state = nm->nodes[id].state & NM_MAIN_MASK;
 
                if (state == NM_MAIN_ON || state == NM_MAIN_LOGIN) {
-                       /* TODO: Check for limp home nodes? */
+                       /* We skip limp home nodes */
                        nm->nodes[nm->my_id].next = id;
                        break;
                }
@@ -94,70 +78,83 @@ static void nm_handle_can_frame(struct NM_Main *nm, struct can_frame *frame)
                return;
        }
 
+       /* If we're currently stuck in Limp Home mode, and we can see
+        * someone else's messages, reset and re-login.
+        */
+       if (nm->nodes[nm->my_id].state == NM_MAIN_LIMPHOME) {
+               nm_reset(nm);
+               return;
+       }
+
        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;
 
-                               nm->tv.tv_sec = 0;
-                               nm->tv.tv_usec = 0;
+                               nm_set_timer_now(nm);
+
                                /* IMPORTANT: The caller needs to check for
-                                * timeouts first, so no other NM frames are
-                                * received until our correcting login has
-                                * been sent.
+                                * timeouts first, i.e. no other NM frames
+                                * are received until our correcting login
+                                * has been sent.
                                 */
                        } else if (next == nm->nodes[nm->my_id].next) {
-                               /* where nm->nodes[nm->my_id].next == nm->my_id */
-
-                               /* TODO: Is this a case we need to handle? */
+                               /* where (nm->nodes[nm->my_id].next == nm->my_id) */
 
                                /* It can happen when:
                                 *  - our sent frames don't go anywhere
                                 *  - we just logged in and immediately
                                 *    afterwards another ECU sent a regular
-                                *    NM frame without knowing that we exist.
+                                *    NM frame.
                                 */
+
+                               /* 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->tv.tv_sec = 0;
-                               nm->tv.tv_usec = NM_USECS_MY_TURN;
+
+                               nm_set_timer_normal(nm);
                        } else {
-                               /* We just got some random ON message.
-                                * Reset the timer looking out for broken
-                                * connectivity.
-                                */
-                               nm->tv.tv_sec = 0;
-                               nm->tv.tv_usec = NM_USECS_OTHER_TURN;
+                               /* We just received a random ON message. */
+
+                               /* Nothing to do. */
                        }
                        break;
                case NM_MAIN_LOGIN:
                        /* Note: sender != nm->my_id */
 
-                       printf("Handling LOGIN\n");
-
-                       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.
                         * Instead, we'll simply include them in the next
-                        * round. */
-
-                       /* Actually, when a login is done as a correction,
-                        * we do reset the timeout.
-                        *
-                        * TODO.
+                        * round.
                         */
+
+                       /* Nothing else to do. */
+                       break;
+               case NM_MAIN_LIMPHOME:
+                       /* Nothing we can do. Poor guy. */
                        break;
        }
 
@@ -182,26 +179,40 @@ 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->tv.tv_sec = 0;
-       nm->tv.tv_usec = NM_USECS_OTHER_TURN;
-
        nm_buildframe(nm, frame);
-}
-
-
 
+       switch(nm->timer_reason) {
+               case NM_TIMER_NOW:
+                       /* We're due to log in */
 
-static void nm_start(struct NM_Main *nm, struct can_frame *frame)
-{
-       nm->tv.tv_sec = 0;
-       nm->tv.tv_usec = 50000;
-
-
-
-       nm->nodes[nm->my_id].next = nm->my_id;
-       nm->nodes[nm->my_id].state = NM_MAIN_LOGIN;
-
-       nm_buildframe(nm, frame);
+                       /* We're going to be ready, let's
+                        * change state (RCD 310 behavior)
+                        */
+                       nm->nodes[nm->my_id].state = NM_MAIN_ON;
+                       nm_set_timer_normal(nm);
+                       break;
+               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;
+                               default:
+                                       printf("BUG: TIMER_NORMAL expired in non-ON state\n");
+                                       break;
+                       }
+                       nm_set_timer_awol(nm);
+                       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:
+                       nm_set_timer_limphome(nm);
+                       break;
+       }
 }
 
 
@@ -257,13 +268,16 @@ int main(int argc, char **argv)
        struct NM_Main *nm;
        fd_set rdfs;
        int s;
+       NM_ID my_id;
 
-       if (argc != 2) {
-               printf("syntax: %s IFNAME\n", argv[0]);
+       if (argc != 3) {
+               printf("syntax: %s IFNAME MY_ID\n", argv[0]);
                return 1;
        }
 
-       nm = nm_alloc(5, 0x0b, 0x420);
+       my_id = strtoul(argv[2], NULL, 0);
+
+       nm = nm_alloc(5, my_id, 0x420);
        if (!nm) {
                printf("Out of memory allocating NM struct.\n");
                return 1;
@@ -271,14 +285,6 @@ int main(int argc, char **argv)
 
        s = net_init(argv[1]);
 
-       /* Stir up the hornet's nest */
-       if (1) {
-               struct can_frame frame;
-
-               nm_start(nm, &frame);
-               can_tx(s, &frame);
-       }
-
        while (1) {
                int retval;
 
@@ -313,6 +319,7 @@ int main(int argc, char **argv)
        }
 
        nm_free(nm);
+       close(s);
 
        return 0;
 }