Reputation: 1668
I am using flexcan driver on an embedded linux and I have C
program controling can messages. In my C
program I need to check the state of the can bus e.g. buss-off
or error-active
. I can use linux command like
ip -details -statistics link show can0
with following result:
2: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UP mode DEFAULT group default qlen 10
link/can promiscuity 0
can state *ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 100
bitrate 250000 sample-point 0.866
tq 266 prop-seg 6 phase-seg1 6 phase-seg2 2 sjw 1
flexcan: tseg1 4..16 tseg2 2..8 sjw 1..4 brp 1..256 brp-inc 1
clock 30000000
re-started bus-errors arbit-lost error-warn error-pass bus-off
31594 0 0 7686 25577 33258
RX: bytes packets errors dropped overrun mcast
5784560 723230 0 1 0 0
TX: bytes packets errors dropped carrier collsns
157896 19742 0 33269 0 0
How can I get that can state ERROR-ACTIVE
in my C program? Also I can see in the flex can driver there are some registers that can be used to see the state but I don't know how to include these values in my program also. registers like FLEXCAN_ESR_BOFF_INT
contains the values that I need.
Upvotes: 5
Views: 14860
Reputation: 135
Just discovered this question and thought I'd share some insights. While my response might be a bit delayed, I hope it proves helpful for anyone grappling with this issue.
It seems the flexcan driver is currently deprecated, and the recommended approach is to use SocketCAN.
As yegorich answered, to check for errors, you need to set the option CAN_RAW_ERR_FILTER, as described in the SocketCAN documentation.
To dive into the details, I set my filter to CAN_ERR_MASK
to catch all errors:
can_err_mask_t err_mask = CAN_ERR_MASK;
setsockopt(s, SOL_CAN_RAW, CAN_RAW_ERR_FILTER, &err_mask, sizeof(err_mask));
Then, I started collecting and printing some valid CAN messages:
can_id: 555 data length: 7 data: AA AA AA AA 55 55 55
can_id: 555 data length: 7 data: AA AA AA AA 55 55 55
Next, I changed the baud rate and sent a message to force a CAN bus error. I received the following messages representing the error:
can_id: 20000004 data length: 8 data: 00 04 00 00 00 00 00 00
can_id: 20000004 data length: 8 data: 00 10 00 00 00 00 00 00
Let's examine these error messages at bit level.
The CAN ID starting with 0x20000000
indicates that the CAN_ERR_FLAG
, defined in include/linux/can.h is set, confirming this is an "error message frame".
Now, by looking at include/linux/can/error.h, we can investigate the error message contents.
There is a section described /* error class (mask) in can_id */
. Here we find:
#define CAN_ERR_CRTL 0x00000004U /* controller problems / data[1] */
This indicates we should examine data[1]
, which has the value 0x04
in the first message and 0x10
in the second message.
Similarly to the CAN ID, there are flags for the data[1]
group, including:
#define CAN_ERR_CRTL_RX_WARNING 0x04 /* reached warning level for RX errors */
and
#define CAN_ERR_CRTL_RX_PASSIVE 0x10 /* reached error passive status RX */
Therefore, by inspecting the contents of the error messages, we can see that the device went to the passive state.
To make it easier for anyone who might be as lost as I was, here is the full code I used to read the CAN messages. I ran it on a Raspberry Pi, and before executing the program, I configured the CAN interface with the following command:
sudo ip link set can0 up type can bitrate 500000 listen-only off
Code:
#include <linux/can.h>
#include <linux/can/raw.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main() {
int s;
struct sockaddr_can addr;
struct ifreq ifr;
s = socket(PF_CAN, SOCK_RAW, CAN_RAW);
if (s < 0) {
perror("Socket creation failed");
exit(EXIT_FAILURE);
}
strcpy(ifr.ifr_name, "can0");
if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
perror("Error in ioctl");
close(s);
exit(EXIT_FAILURE);
}
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
perror("Error in bind");
close(s);
exit(EXIT_FAILURE);
}
can_err_mask_t err_mask = CAN_ERR_MASK;
setsockopt(s, SOL_CAN_RAW, CAN_RAW_ERR_FILTER, &err_mask, sizeof(err_mask));
while (1) {
struct can_frame frame;
int nbytes = read(s, &frame, sizeof(struct can_frame));
if (nbytes < 0) {
perror("Error while reading from the socket");
close(s);
exit(EXIT_FAILURE);
}
printf("can_id: %X data length: %d data:", frame.can_id, frame.len);
for (int i = 0; i < frame.len; i++) {
printf(" %02X", frame.data[i]);
}
printf("\n");
}
close(s);
return 0;
}
Upvotes: 1
Reputation: 4849
You can setup your socket to return CAN errors as messages.
As described in Network Problem Notifications the CAN interface driver can generate so called Error Message Frames that can optionally be passed to the user application in the same way as other CAN frames. The possible errors are divided into different error classes that may be filtered using the appropriate error mask. To register for every possible error condition CAN_ERR_MASK can be used as value for the error mask. The values for the error mask are defined in linux/can/error.h
can_err_mask_t err_mask = ( CAN_ERR_TX_TIMEOUT | CAN_ERR_BUSOFF );
setsockopt(s, SOL_CAN_RAW, CAN_RAW_ERR_FILTER,
&err_mask, sizeof(err_mask));
See kernel documentation for more information.
Update
Take a look at libsocketcan
and the routine can_get_state.
Upvotes: 5