PHA
PHA

Reputation: 1668

How check bus state in socketcan

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

Answers (2)

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

yegorich
yegorich

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

Related Questions