2 * Copyright 2015-2016 Max Staudt
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License 2 as published
6 * by the Free Software Foundation.
16 #include <sys/types.h>
17 #include <sys/socket.h>
18 #include <linux/can.h>
19 #include <linux/can/raw.h>
21 #include <sys/ioctl.h>
27 #include "revag-nm-tools.h"
33 static void nm_update_my_next_id(struct NM_Main *nm)
35 unsigned id = nm->my_id;
41 if (id >= nm->max_nodes) {
45 state = nm->nodes[id].state & NM_MAIN_MASK;
47 if (state == NM_MAIN_ON || state == NM_MAIN_LOGIN) {
48 /* We skip limp home nodes */
49 nm->nodes[nm->my_id].next = id;
52 } while (id != nm->my_id);
58 static unsigned nm_num_active_nodes(struct NM_Main *nm)
63 for (id = 0; id < nm->max_nodes; id++) {
66 state = nm->nodes[id].state & NM_MAIN_MASK;
68 if (state == NM_MAIN_ON || state == NM_MAIN_LOGIN) {
69 /* We skip limp home nodes */
79 static void nm_handle_can_frame(struct NM_Main *nm, struct can_frame *frame)
84 /* Is this a valid frame within our logical network? */
85 if (!nm_is_rx_frame_valid(nm, frame)) {
89 printf("Received NM frame from CAN ID %03x\n", frame->can_id);
92 /* Parse sender, its perceived successor, and its state */
93 sender = frame->can_id & (nm->max_nodes - 1);
94 next = frame->data[0];
95 state = frame->data[1];
97 /* TODO: Validate state, it needs to be within the enum */
99 /* Skip our own frames */
100 if (sender == nm->my_id) {
104 /* If we're currently stuck in Limp Home mode, and we can see
105 * someone else's messages: reset counters, reset NM, re-login.
107 if ((nm->nodes[nm->my_id].state & NM_MAIN_MASK)
108 == NM_MAIN_LIMPHOME) {
113 nm->nodes[sender].next = next;
114 nm->nodes[sender].state = state;
116 /* Update our view of the world */
117 nm_update_my_next_id(nm);
119 switch (state & NM_MAIN_MASK) {
121 /* We're not alone, so let's transition to ON for now.
123 nm->nodes[nm->my_id].state = NM_MAIN_ON;
125 /* The AWOL timeout is ONLY reset on
126 * NM_MAIN_ON messages.
128 nm_set_timer_awol(nm);
130 if (next == nm->nodes[nm->my_id].next
131 && nm->nodes[nm->my_id].next != nm->my_id) {
132 /* Sender doesn't know we exist */
134 nm->nodes[nm->my_id].state = NM_MAIN_LOGIN;
136 nm_set_timer_now(nm);
138 /* IMPORTANT: The caller needs to check for
139 * timeouts first, i.e. no other NM frames
140 * are received until our correcting login
143 } else if (next == nm->nodes[nm->my_id].next) {
144 /* where (nm->nodes[nm->my_id].next == nm->my_id) */
146 /* It can happen when:
147 * - our sent frames don't go anywhere
148 * - we just logged in and immediately
149 * afterwards another ECU sent a regular
154 } else if (next == nm->my_id) {
155 /* It's our turn, do a normal timeout.
156 * This is a period in which anyone we missed
157 * can send its re-login frame to correct us.
160 nm_set_timer_normal(nm);
162 /* We just received a random ON message. */
168 /* Note: sender != nm->my_id */
170 /* We're not alone anymore, so let's change state. */
171 nm->nodes[nm->my_id].state = NM_MAIN_ON;
173 /* We don't reset the timeout when somebody logs in.
174 * Instead, we'll simply include them in the next
179 * Special case: The Media-In's NM implementation
180 * doesn't auto-switch to NM_ON. Let's say hello,
181 * even if it ends up being a little late.
183 if (nm_num_active_nodes(nm) >= 2) {
184 nm_set_timer_normal(nm);
187 /* Nothing else to do. */
189 case NM_MAIN_LIMPHOME:
190 /* Nothing we can do. Poor guy. */
202 static void nm_buildframe(struct NM_Main *nm, struct can_frame *frame)
204 frame->can_id = nm->can_base + nm->my_id;
206 frame->data[0] = nm->nodes[nm->my_id].next;
207 frame->data[1] = nm->nodes[nm->my_id].state;
213 static void nm_timeout_callback(struct NM_Main *nm, struct can_frame *frame)
215 switch(nm->timer_reason) {
217 /* We're due to log in */
218 nm_buildframe(nm, frame);
220 if ((nm->nodes[nm->my_id].state & NM_MAIN_MASK)
223 printf("BUG: TIMER_NOW expired in non-ON state %u\n",
224 nm->nodes[nm->my_id].state & NM_MAIN_MASK);
227 /* We're going to be ready, let's
228 * change state (RCD 310 behavior)
230 nm->nodes[nm->my_id].state = NM_MAIN_ON;
232 nm_set_timer_normal(nm);
234 case NM_TIMER_NORMAL:
235 /* We're due to send our own ring message */
236 nm_buildframe(nm, frame);
238 if ((nm->nodes[nm->my_id].state & NM_MAIN_MASK)
241 printf("BUG: TIMER_NORMAL expired in non-ON state %u\n",
242 nm->nodes[nm->my_id].state & NM_MAIN_MASK);
245 nm_set_timer_awol(nm);
248 /* The network is silent because a node disappeared
249 * or something bad happened.
250 * Reset everything and start over.
253 nm_buildframe(nm, frame);
255 case NM_TIMER_LIMPHOME:
256 printf("Limp home timer expired again :(\n");
258 nm_buildframe(nm, frame);
259 nm_set_timer_limphome(nm);
267 static int net_init(char *ifname)
271 struct sockaddr_can addr;
273 struct can_filter fi;
275 s = socket(PF_CAN, SOCK_RAW, CAN_RAW);
281 /* Convert interface name to index */
282 memset(&ifr.ifr_name, 0, sizeof(ifr.ifr_name));
283 strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
284 if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
285 perror("SIOCGIFINDEX");
289 /* Open the CAN interface */
290 memset(&addr, 0, sizeof(addr));
291 addr.can_family = AF_CAN;
292 addr.can_ifindex = ifr.ifr_ifindex;
293 if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
298 recv_own_msgs = 1; /* 0 = disabled (default), 1 = enabled */
299 setsockopt(s, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS,
300 &recv_own_msgs, sizeof(recv_own_msgs));
302 /* Handle only 32 NM IDs at CAN base ID 0x420 */
306 setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &fi, sizeof(struct can_filter));
312 int main(int argc, char **argv)
320 printf("syntax: %s IFNAME MY_ID\n", argv[0]);
324 my_id = strtoul(argv[2], NULL, 0);
326 nm = nm_alloc(5, my_id, 0x420);
328 printf("Out of memory allocating NM struct.\n");
332 s = net_init(argv[1]);
340 retval = select(s+1, &rdfs, NULL, NULL, &nm->tv);
341 /* We currently rely on Linux timeout behavior here,
342 * i.e. the timeout now reflects the remaining time */
346 } else if (!retval) {
347 /* Timeout, we NEED to check this first */
348 struct can_frame frame;
350 nm_timeout_callback(nm, &frame);
352 } else if (FD_ISSET(s, &rdfs)) {
353 struct can_frame frame;
356 ret = read(s, &frame, sizeof(frame));
358 perror("recvfrom CAN socket");
362 nm_handle_can_frame(nm, &frame);