-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) {
+static void nm_update_my_next_id(struct NM_Main *nm)
+{
unsigned id = nm->my_id;
do {
break;
}
} while (id != nm->my_id);
+}
+
+
+
- if (nm->nodes[nm->my_id].next == nm->my_id) {
- /* Uh oh, we're the only one left. */
+static unsigned nm_num_active_nodes(struct NM_Main *nm)
+{
+ unsigned id = 0;
+ unsigned active = 0;
+
+ for (id = 0; id < nm->max_nodes; id++) {
+ NM_State state;
- /* TODO */
- nm->nodes[nm->my_id].state = NM_MAIN_LOGIN;
+ state = nm->nodes[id].state & NM_MAIN_MASK;
- /* TODO: Timeout 140ms (RCD 310) */
+ if (state == NM_MAIN_ON || state == NM_MAIN_LOGIN) {
+ /* We skip limp home nodes */
+ active++;
+ }
}
+
+ return active;
}
return;
}
+ /* If we're currently stuck in Limp Home mode, and we can see
+ * someone else's messages: reset counters, reset NM, re-login.
+ */
+ if ((nm->nodes[nm->my_id].state & NM_MAIN_MASK)
+ == NM_MAIN_LIMPHOME) {
+ nm_initreset(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, i.e. no other NM frames
* are received until our correcting login
* 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->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_NODE_AWOL;
+ /* 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. */
+ * round.
+ */
- /* Actually, when a login is done as a correction,
- * we do reset the timeout.
+ /* HACK:
+ * Special case: The Media-In's NM implementation
+ * doesn't auto-switch to NM_ON. Let's say hello,
+ * even if it ends up being a little late.
*/
- if (next != sender) {
- nm->tv.tv_sec = 0;
- nm->tv.tv_usec = NM_USECS_NODE_AWOL;
+ if (nm_num_active_nodes(nm) >= 2) {
+ nm_set_timer_normal(nm);
}
+
+ /* Nothing else to do. */
break;
case NM_MAIN_LIMPHOME:
- nm_update_my_next_id(nm);
+ /* Nothing we can do. Poor guy. */
+ break;
}
nm_dump_all(nm);
static void nm_timeout_callback(struct NM_Main *nm, struct can_frame *frame)
{
- nm->tv.tv_sec = 0;
- nm->tv.tv_usec = NM_USECS_NODE_AWOL;
-
- nm_buildframe(nm, frame);
-}
+ switch(nm->timer_reason) {
+ case NM_TIMER_NOW:
+ /* We're due to log in */
+ nm_buildframe(nm, frame);
+ if ((nm->nodes[nm->my_id].state & NM_MAIN_MASK)
+ != NM_MAIN_LOGIN) {
+ printf("BUG: TIMER_NOW expired in non-ON state %u\n",
+ nm->nodes[nm->my_id].state & NM_MAIN_MASK);
+ }
+ /* We're going to be ready, let's
+ * change state (RCD 310 behavior)
+ */
+ nm->nodes[nm->my_id].state = NM_MAIN_ON;
-static void nm_start(struct NM_Main *nm, struct can_frame *frame)
-{
- nm->tv.tv_sec = 0;
- nm->tv.tv_usec = 50000;
+ nm_set_timer_normal(nm);
+ break;
+ case NM_TIMER_NORMAL:
+ /* We're due to send our own ring message */
+ nm_buildframe(nm, frame);
+ if ((nm->nodes[nm->my_id].state & NM_MAIN_MASK)
+ != NM_MAIN_ON) {
+ printf("BUG: TIMER_NORMAL expired in non-ON state %u\n",
+ nm->nodes[nm->my_id].state & NM_MAIN_MASK);
+ }
- nm->nodes[nm->my_id].next = nm->my_id;
- nm->nodes[nm->my_id].state = NM_MAIN_LOGIN;
+ 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);
+ nm_buildframe(nm, frame);
+ break;
+ case NM_TIMER_LIMPHOME:
+ printf("Limp home timer expired again :(\n");
- nm_buildframe(nm, frame);
+ nm_buildframe(nm, frame);
+ nm_set_timer_limphome(nm);
+ break;
+ }
}
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;
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;
}
nm_free(nm);
+ close(s);
return 0;
}