Search code examples
clinuxcan-bus

Reading CAN bus in C displays incorrect CAN ID for 29-bit CAN ID


I wrote some code in C to read CAN bus data. Everything works correctly when I read 11-bit CAN IDs. As soon as I try to read 29-bit IDs it displays the ID incorrectly.

Example:

Receiving message with 29-bit ID:

0x01F0A020

And print it with

printf("%X\n", frame.can_id);

it prints 81F0A020.

11-bit ID message

0x7DF

and print it with

printf("%X\n", frame.can_id);

It prints correctly 7DF.

Why is this the case?

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <net/if.h>
#include <fcntl.h>
#include <inttypes.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <linux/can.h>
#include <linux/can/raw.h>

#define MAX_DATA_LEN 8
#define MAX_FIELDS 23
#define MAX_FIELD_LEN 64
#include <limits.h>

char data_str[MAX_FIELDS][MAX_FIELD_LEN];
int i;

int
main(void)
{
    int s;
    int nbytes;
    struct sockaddr_can addr;
    struct can_frame frame;
    unsigned short data[MAX_FIELDS];
    int sockfd = 0;
    int bcast = 1;
    struct sockaddr_in src_addr;
    struct sockaddr_in dst_addr;
    int numbytes;

    int fa;
    struct ifreq ifr;

    fa = socket(AF_INET, SOCK_DGRAM, 0);

    ifr.ifr_addr.sa_family = AF_INET;
    strncpy(ifr.ifr_name, "eth0", IFNAMSIZ-1);

    ioctl(fa, SIOCGIFHWADDR, &ifr);
    close(fa);

    //
    if((sockfd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
    {
        perror("Udp sockfd create failed");
        exit(1);
    }

    //Enabled broadcast mode for udp
    if((setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST,
                &bcast, sizeof bcast)) == -1)
    {
        perror("setsockopt failed for broadcast mode ");
        exit(1);
    }

    src_addr.sin_family = AF_INET;
    src_addr.sin_port = htons(8888);
    src_addr.sin_addr.s_addr = INADDR_ANY;
    memset(src_addr.sin_zero, '\0', sizeof src_addr.sin_zero);

    if(bind(sockfd, (struct sockaddr*) &src_addr, sizeof src_addr) == -1)
    {
        perror("bind");
        exit(1);
    }

    dst_addr.sin_family = AF_INET;
    dst_addr.sin_port = htons(45454);
    dst_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    memset(dst_addr.sin_zero, '\0', sizeof dst_addr.sin_zero);

    if((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
        perror("Error while opening socket");
        return -1;
    }

    addr.can_family  = AF_CAN;
    addr.can_ifindex = 0;

    if(bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        perror("Error in socket bind");
        return -2;
    }

    //struct can_frame frame;
    while(1)
    {
        nbytes = read(s, &frame, sizeof(struct can_frame));

        if (nbytes < 0) {
                perror("can raw socket read");
                return 1;
        }

        /* Paranoid check ... */
        if (nbytes < sizeof(struct can_frame)) {
                fprintf(stderr, "read: incomplete CAN frame\n");
                return 1;
        }

        // Print the received CAN ID
        printf("%X\n", frame.can_id);
    }
    return 0;
}

Solution

  • The field can_id of struct can_frame contains the CAN ID and the EFF/RTR/ERR flags. The extended ID has 29 bits, so there are 3 free bits that are used to represent 3 flags.

    Your example ID 0x01F0A020 must be an extended frame, but ID 0x7DF can be sent as a base frame or as an extended frame. These are different messages. To distinguish between a base frame or an extended frame with the same ID you need the EFF flag.

    In your example you see the value 0x81F0A020 which is the combination of ID 0x01F0A020 and CAN_EFF_FLAG (0x80000000U).

    Extract from https://github.com/torvalds/linux/blob/master/include/uapi/linux/can.h

    /* special address description flags for the CAN_ID */
    #define CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */
    #define CAN_RTR_FLAG 0x40000000U /* remote transmission request */
    #define CAN_ERR_FLAG 0x20000000U /* error message frame */
    
    /* valid bits in CAN ID for frame formats */
    #define CAN_SFF_MASK 0x000007FFU /* standard frame format (SFF) */
    #define CAN_EFF_MASK 0x1FFFFFFFU /* extended frame format (EFF) */
    #define CAN_ERR_MASK 0x1FFFFFFFU /* omit EFF, RTR, ERR flags */
    

    ...

    /**
     * struct can_frame - basic CAN frame structure
     * @can_id:  CAN ID of the frame and CAN_*_FLAG flags, see canid_t definition
     * @can_dlc: frame payload length in byte (0 .. 8) aka data length code
     *           N.B. the DLC field from ISO 11898-1 Chapter 8.4.2.3 has a 1:1
     *           mapping of the 'data length code' to the real payload length
     * @__pad:   padding
     * @__res0:  reserved / padding
     * @__res1:  reserved / padding
     * @data:    CAN frame payload (up to 8 byte)
     */
    struct can_frame {
        canid_t can_id;  /* 32 bit CAN_ID + EFF/RTR/ERR flags */
        __u8    can_dlc; /* frame payload length in byte (0 .. CAN_MAX_DLEN) */
        __u8    __pad;   /* padding */
        __u8    __res0;  /* reserved / padding */
        __u8    __res1;  /* reserved / padding */
        __u8    data[CAN_MAX_DLEN] __attribute__((aligned(8)));
    };
    

    To get the ID only without flags, you should apply CAN_SFF_MASK or CAN_EFF_MASK depending on the value of the CAN_EFF_FLAG bit.

    Example code:

            //Print the received CAN ID 
            printf("%X\n", 
                   (frame.can_id & CAN_EFF_FLAG) ? (frame.can_id & CAN_EFF_MASK)
                                                 : (frame.can_id & CAN_SFF_MASK));