From: norly Date: Sat, 18 Mar 2017 23:23:49 +0000 (+0100) Subject: Implement NMReset in case a node goes missing X-Git-Url: https://git.enpas.org/?p=revag-nm.git;a=commitdiff_plain;h=6357000d97517389e2b15f8a7b01c4e83ef0d96f Implement NMReset in case a node goes missing --- diff --git a/vw-nm-tools.h b/vw-nm-tools.h index 426e778..f4126a6 100644 --- a/vw-nm-tools.h +++ b/vw-nm-tools.h @@ -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 6a3ca05..c843902 100644 --- a/vw-nm.c +++ b/vw-nm.c @@ -28,48 +28,6 @@ -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 2c38e72..fcb6b4b 100644 --- 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