1 /*
2 * CAN module object for Linux socketCAN Error handling.
3 *
4 * @file CO_error.c
5 * @ingroup CO_driver
6 * @author Martin Wagner
7 * @copyright 2018 - 2020 Neuberger Gebaeudeautomation GmbH
8 *
9 *
10 * This file is part of CANopenNode, an opensource CANopen Stack.
11 * Project home page is <https://github.com/CANopenNode/CANopenNode>.
12 * For more information on CANopen see <http://www.can-cia.org/>.
13 *
14 * Licensed under the Apache License, Version 2.0 (the "License");
15 * you may not use this file except in compliance with the License.
16 * You may obtain a copy of the License at
17 *
18 * http://www.apache.org/licenses/LICENSE-2.0
19 *
20 * Unless required by applicable law or agreed to in writing, software
21 * distributed under the License is distributed on an "AS IS" BASIS,
22 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23 * See the License for the specific language governing permissions and
24 * limitations under the License.
25 */
26
27 #include <string.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <linux/can/error.h>
31
32 #include "CO_error.h"
33
34 #if defined CO_DRIVER_ERROR_REPORTING
35 #if __has_include("syslog/log.h")
36 #include "syslog/log.h"
37 #include "msgs.h"
38 #else
39 #include "CO_msgs.h"
40 #endif
41 #else
42 #define log_printf(macropar_prio, macropar_message, ...)
43 #endif
44
45
46 /**
47 * Reset CAN interface and set to listen only mode
48 */
CO_CANerrorSetListenOnly(CO_CANinterfaceErrorhandler_t * CANerrorhandler,bool_t resetIf)49 static CO_CANinterfaceState_t CO_CANerrorSetListenOnly(
50 CO_CANinterfaceErrorhandler_t *CANerrorhandler,
51 bool_t resetIf)
52 {
53 char command[100];
54
55 log_printf(LOG_DEBUG, DBG_CAN_SET_LISTEN_ONLY, CANerrorhandler->ifName);
56
57 clock_gettime(CLOCK_MONOTONIC, &CANerrorhandler->timestamp);
58 CANerrorhandler->listenOnly = true;
59
60 if (resetIf) {
61 snprintf(command, sizeof(command), "ip link set %s down && "
62 "ip link set %s up "
63 "&",
64 CANerrorhandler->ifName,
65 CANerrorhandler->ifName);
66 system(command);
67 }
68
69 return CO_INTERFACE_LISTEN_ONLY;
70 }
71
72
73 /**
74 * Clear listen only
75 */
CO_CANerrorClearListenOnly(CO_CANinterfaceErrorhandler_t * CANerrorhandler)76 static void CO_CANerrorClearListenOnly(
77 CO_CANinterfaceErrorhandler_t *CANerrorhandler)
78 {
79 log_printf(LOG_DEBUG, DBG_CAN_CLR_LISTEN_ONLY, CANerrorhandler->ifName);
80
81 CANerrorhandler->listenOnly = false;
82 CANerrorhandler->timestamp.tv_sec = 0;
83 CANerrorhandler->timestamp.tv_nsec = 0;
84 }
85
86
87 /**
88 * Check and handle "bus off" state
89 */
CO_CANerrorBusoff(CO_CANinterfaceErrorhandler_t * CANerrorhandler,const struct can_frame * msg)90 static CO_CANinterfaceState_t CO_CANerrorBusoff(
91 CO_CANinterfaceErrorhandler_t *CANerrorhandler,
92 const struct can_frame *msg)
93 {
94 CO_CANinterfaceState_t result = CO_INTERFACE_ACTIVE;
95
96 if ((msg->can_id & CAN_ERR_BUSOFF) != 0) {
97 log_printf(LOG_NOTICE, CAN_BUSOFF, CANerrorhandler->ifName);
98
99 /* The can interface changed it's state to "bus off" (e.g. because of
100 * a short on the can wires). We re-start the interface and mark it
101 * "listen only".
102 * Restarting the interface is the only way to clear kernel and hardware
103 * tx queues */
104 result = CO_CANerrorSetListenOnly(CANerrorhandler, true);
105 }
106 return result;
107 }
108
109
110 /**
111 * Check and handle controller problems
112 */
CO_CANerrorCrtl(CO_CANinterfaceErrorhandler_t * CANerrorhandler,const struct can_frame * msg)113 static CO_CANinterfaceState_t CO_CANerrorCrtl(
114 CO_CANinterfaceErrorhandler_t *CANerrorhandler,
115 const struct can_frame *msg)
116 {
117 CO_CANinterfaceState_t result = CO_INTERFACE_ACTIVE;
118
119 /* Control
120 * - error counters (rec/tec) are handled inside CAN hardware, nothing
121 * to do in here
122 * - we can't really do anything about buffer overflows here. Confirmed
123 * CANopen protocols will detect the error, non-confirmed protocols
124 * need to be error tolerant */
125 if ((msg->can_id & CAN_ERR_CRTL) != 0) {
126 if ((msg->data[1] & CAN_ERR_CRTL_RX_PASSIVE) != 0) {
127 log_printf(LOG_NOTICE, CAN_RX_PASSIVE, CANerrorhandler->ifName);
128 }
129 else if ((msg->data[1] & CAN_ERR_CRTL_TX_PASSIVE) != 0) {
130 log_printf(LOG_NOTICE, CAN_TX_PASSIVE, CANerrorhandler->ifName);
131 }
132 else if ((msg->data[1] & CAN_ERR_CRTL_RX_OVERFLOW) != 0) {
133 log_printf(LOG_NOTICE, CAN_RX_BUF_OVERFLOW, CANerrorhandler->ifName);
134 }
135 else if ((msg->data[1] & CAN_ERR_CRTL_TX_OVERFLOW) != 0) {
136 log_printf(LOG_NOTICE, CAN_TX_BUF_OVERFLOW, CANerrorhandler->ifName);
137 }
138 else if ((msg->data[1] & CAN_ERR_CRTL_RX_WARNING) != 0) {
139 log_printf(LOG_INFO, CAN_RX_LEVEL_WARNING, CANerrorhandler->ifName);
140 }
141 else if ((msg->data[1] & CAN_ERR_CRTL_TX_WARNING) != 0) {
142 log_printf(LOG_INFO, CAN_TX_LEVEL_WARNING, CANerrorhandler->ifName);
143 }
144 else if ((msg->data[1] & CAN_ERR_CRTL_ACTIVE) != 0) {
145 log_printf(LOG_NOTICE, CAN_TX_LEVEL_ACTIVE, CANerrorhandler->ifName);
146 }
147 }
148 return result;
149 }
150
151
152 /**
153 * Check and handle controller problems
154 */
CO_CANerrorNoack(CO_CANinterfaceErrorhandler_t * CANerrorhandler,const struct can_frame * msg)155 static CO_CANinterfaceState_t CO_CANerrorNoack(
156 CO_CANinterfaceErrorhandler_t *CANerrorhandler,
157 const struct can_frame *msg)
158 {
159 CO_CANinterfaceState_t result = CO_INTERFACE_ACTIVE;
160
161 if (CANerrorhandler->listenOnly) {
162 return CO_INTERFACE_LISTEN_ONLY;
163 }
164
165 /* received no ACK on transmission */
166 if ((msg->can_id & CAN_ERR_ACK) != 0) {
167 CANerrorhandler->noackCounter ++;
168 if (CANerrorhandler->noackCounter > CO_CANerror_NOACK_MAX) {
169 log_printf(LOG_INFO, CAN_NOACK, CANerrorhandler->ifName);
170
171 /* We get the NO-ACK error continuously when no other CAN node
172 * is active on the bus (Error Counting exception 1 in CAN spec).
173 * todo - you need to pull the message causing no-ack from the CAN
174 * hardware buffer. This can be done by either resetting interface
175 * in here or deleting it within Linux Kernel can driver (set "false"). */
176 result = CO_CANerrorSetListenOnly(CANerrorhandler, true);
177 }
178 }
179 else {
180 CANerrorhandler->noackCounter = 0;
181 }
182 return result;
183 }
184
185
186 /******************************************************************************/
CO_CANerror_init(CO_CANinterfaceErrorhandler_t * CANerrorhandler,int fd,const char * ifName)187 void CO_CANerror_init(
188 CO_CANinterfaceErrorhandler_t *CANerrorhandler,
189 int fd,
190 const char *ifName)
191 {
192 if (CANerrorhandler == NULL) {
193 return;
194 }
195
196 CANerrorhandler->fd = fd;
197 memcpy(CANerrorhandler->ifName, ifName, sizeof(CANerrorhandler->ifName));
198 CANerrorhandler->noackCounter = 0;
199 CANerrorhandler->listenOnly = false;
200 CANerrorhandler->timestamp.tv_sec = 0;
201 CANerrorhandler->timestamp.tv_nsec = 0;
202 }
203
204
205 /******************************************************************************/
CO_CANerror_disable(CO_CANinterfaceErrorhandler_t * CANerrorhandler)206 void CO_CANerror_disable(
207 CO_CANinterfaceErrorhandler_t *CANerrorhandler)
208 {
209 if (CANerrorhandler == NULL) {
210 return;
211 }
212
213 memset(CANerrorhandler, 0, sizeof(*CANerrorhandler));
214 CANerrorhandler->fd = -1;
215 }
216
217
218 /******************************************************************************/
CO_CANerror_rxMsg(CO_CANinterfaceErrorhandler_t * CANerrorhandler)219 void CO_CANerror_rxMsg(
220 CO_CANinterfaceErrorhandler_t *CANerrorhandler)
221 {
222 if (CANerrorhandler == NULL) {
223 return;
224 }
225
226 /* someone is active, we can leave listen only immediately */
227 if (CANerrorhandler->listenOnly) {
228 CO_CANerrorClearListenOnly(CANerrorhandler);
229 }
230 CANerrorhandler->noackCounter = 0;
231 }
232
233
234 /******************************************************************************/
CO_CANerror_txMsg(CO_CANinterfaceErrorhandler_t * CANerrorhandler)235 CO_CANinterfaceState_t CO_CANerror_txMsg(
236 CO_CANinterfaceErrorhandler_t *CANerrorhandler)
237 {
238 struct timespec now;
239
240 if (CANerrorhandler == NULL) {
241 return CO_INTERFACE_BUS_OFF;
242 }
243
244 if (CANerrorhandler->listenOnly) {
245 clock_gettime(CLOCK_MONOTONIC, &now);
246 if (CANerrorhandler->timestamp.tv_sec + CO_CANerror_LISTEN_ONLY < now.tv_sec) {
247 /* let's try that again. Maybe someone is waiting for LSS now. It
248 * doesn't matter which message is sent, as all messages are ACKed. */
249 CO_CANerrorClearListenOnly(CANerrorhandler);
250 return CO_INTERFACE_ACTIVE;
251 }
252 return CO_INTERFACE_LISTEN_ONLY;
253 }
254 return CO_INTERFACE_ACTIVE;
255 }
256
257
258 /******************************************************************************/
CO_CANerror_rxMsgError(CO_CANinterfaceErrorhandler_t * CANerrorhandler,const struct can_frame * msg)259 CO_CANinterfaceState_t CO_CANerror_rxMsgError(
260 CO_CANinterfaceErrorhandler_t *CANerrorhandler,
261 const struct can_frame *msg)
262 {
263 if (CANerrorhandler == NULL) {
264 return CO_INTERFACE_BUS_OFF;
265 }
266
267 CO_CANinterfaceState_t result;
268
269 /* Log all error messages in full to debug log, even if analysis is done
270 * further on. */
271 log_printf(LOG_DEBUG, DBG_CAN_ERROR_GENERAL, (int)msg->can_id,
272 msg->data[0], msg->data[1], msg->data[2], msg->data[3],
273 msg->data[4], msg->data[5], msg->data[6], msg->data[7],
274 CANerrorhandler->ifName);
275
276 /* Process errors - start with the most unambiguous one */
277
278 result = CO_CANerrorBusoff(CANerrorhandler, msg);
279 if (result != CO_INTERFACE_ACTIVE) {
280 return result;
281 }
282
283 result = CO_CANerrorCrtl(CANerrorhandler, msg);
284 if (result != CO_INTERFACE_ACTIVE) {
285 return result;
286 }
287
288 result = CO_CANerrorNoack(CANerrorhandler, msg);
289 if (result != CO_INTERFACE_ACTIVE) {
290 return result;
291 }
292
293 return CO_INTERFACE_ACTIVE;
294 }
295