+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) {
unsigned id = nm->my_id;
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;
}
} while (id != nm->my_id);
+
+ if (nm->nodes[nm->my_id].next == nm->my_id) {
+ /* Uh oh, we're the only one left. */
+
+ /* TODO */
+ nm->nodes[nm->my_id].state = NM_MAIN_LOGIN;
+
+ /* TODO: Timeout 140ms (RCD 310) */
+ }
}
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.
+ */
+
+ /* 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;
} 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.
*/
- 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;
+ nm_set_timer_awol(nm);
}
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.
+ /* We don't reset the timeout when somebody logs in,
+ * i.e. (next == sender).
* 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.
*/
+ if (next != sender) {
+ nm_set_timer_awol(nm);
+ }
break;
+ case NM_MAIN_LIMPHOME:
+ nm_update_my_next_id(nm);
}
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_OTHER_TURN;
+ 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 = 50000;
+ nm->tv.tv_usec = NM_USECS_NODE_AWOL;
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;
}
nm_free(nm);
+ close(s);
return 0;
}