i.MX8M Plus FlexCAN: CAN FD setsockopt() fails in C code but works in Python

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

i.MX8M Plus FlexCAN: CAN FD setsockopt() fails in C code but works in Python

421 Views
abhayaugustin1
Contributor II

Hardware: i.MX8M Plus EVK
Kernel: [Your kernel version - check with uname -r]
Interface: FlexCAN (can0/can1)

Issue Summary: CAN FD socket configuration fails in C code with EINVAL (errno 22) but identical operation succeeds in Python on the same system.

Problem Details:

  • Interface properly configured with CAN FD support:

    ip link set can0 up type can bitrate 500000 dbitrate 2000000 fd on
    ip -details link show can0  # Shows <FD> flag and dbitrate parameters
  • Python succeeds:

    import socket
    sock = socket.socket(socket.PF_CAN, socket.SOCK_RAW, socket.CAN_RAW)
    sock.setsockopt(101, 5, 1)  # SOL_CAN_RAW, CAN_RAW_FD_FRAMES - SUCCESS
  • C code fails with identical parameters:

    int sock = socket(PF_CAN, SOCK_RAW, CAN_RAW);
    int enable = 1;
    setsockopt(sock, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &enable, sizeof(enable));
    // Returns -1, errno = 22 (EINVAL)

Debugging Performed:

  • Verified constants: SOL_CAN_RAW=101, CAN_RAW_FD_FRAMES=5 (both languages)
  • Interface shows proper CAN FD configuration with dbitrate
  • Both interfaces (can0, can1) exhibit same behavior
  • Raw socket test (no interface binding) also fails in C
  • Hardware analyzer (PCAN) confirms CAN FD frames transmitted/received

Question: Is there a known issue with FlexCAN CAN FD socket configuration in C applications on i.MX8M Plus? Are there specific compilation flags, kernel modules, or driver configurations required for C programs to enable CAN FD frames via setsockopt()?

System Info:

can0: <FD> state ERROR-ACTIVE (berr-counter tx 0 rx 0)
      bitrate 500000 sample-point 0.875
      dbitrate 2000000 dsample-point 0.750
      flexcan: dtseg1 2..39 dtseg2 2..8 dsjw 1..4 dbrp 1..1024

Any insights or recommendations would be greatly appreciated.

Additional Context: This is for a Power Distribution Unit (PDU) firmware project requiring CAN FD with BRS (Bit Rate Switching) for high-speed data transmission (225 frames every 500ms).

Thanks for your support!

0 Kudos
Reply
6 Replies

324 Views
danielchen
NXP TechSupport
NXP TechSupport

Hi 

 

CAN-FD is not supported on the consumer version of i.MX 8M Plus,   it is only available  on the Industrial version.

Unfortunately on the 8MP EVK,   the consumer version  is installed,  so i.MX 8M Plus EVK not support CAN-FD feature.

 

Regards

Daniel

0 Kudos
Reply

315 Views
abhayaugustin1
Contributor II

Hi,

Actually i think we are having industrial version of i.MX 8M Plus we have checked via pyhton script we can see canfd frames in candump can0 command .
But when i use my c program to open a socket and enable canfd frames it's not happening and receiving error code 22.

Currently i am using :

socket(PF_CAN, SOCK_RAW , CAN_RAW);
setsockopt(sock, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &enable_canfd, sizeof(enable_canfd));
setsockopt is giving me error code 22 (EINVAL).

 

0 Kudos
Reply

312 Views
danielchen
NXP TechSupport
NXP TechSupport

what is the version number of your board?  can you send me the scripts you are using for me to reproduce this issue on my side?  if there is a bug, I will report it to our software team.  Thanks.

0 Kudos
Reply

291 Views
abhayaugustin1
Contributor II

Hi, 
We are using MIMX8ML4CVNKZAB part. 
I have attached python script with the query.
This is the C-program configurations :
socket(PF_CAN, SOCK_RAW , CAN_RAW);

setsockopt(sock, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &enable_canfd, sizeof(enable_canfd));
setsockopt is giving me error code 22 (EINVAL).

Unable to attach the python script so i am pasting it:

#!/usr/bin/env python3
"""
CAN FD Listener Script for i.MX8
Opens socket and listens for CAN FD frames on can0
Similar to your PDU firmware's receive thread functionality
"""

import socket
import struct
import time
import signal
import sys
import os
from datetime import datetime

# CAN constants
SOL_CAN_RAW = 101
CAN_RAW_FD_FRAMES = 5
CAN_RAW_FILTER = 1

# CAN frame flags
CAN_EFF_FLAG = 0x80000000  # Extended frame format
CAN_RTR_FLAG = 0x40000000  # Remote transmission request
CAN_ERR_FLAG = 0x20000000  # Error frame

# CAN FD flags
CANFD_BRS = 0x01  # Bit Rate Switch
CANFD_ESI = 0x02  # Error State Indicator  
CANFD_FDF = 0x04  # FD Format indicator

class CANFDListener:
    """CAN FD frame listener - similar to your PDU receive thread"""
    
    def __init__(self, interface="can0", bitrate=500000, data_bitrate=2000000):
        self.interface = interface
        self.bitrate = bitrate
        self.data_bitrate = data_bitrate
        self.socket = None
        self.running = False
        self.frame_count = 0
        self.start_time = None
        
        # Statistics
        self.can20_frames = 0
        self.canfd_frames = 0
        self.error_frames = 0
        
    def setup_interface(self):
        """Configure CAN interface - like your PDU initialization"""
        print(f"🔧 Setting up {self.interface}...")
        
        try:
            # Bring interface down
            os.system(f"ip link set {self.interface} down 2>/dev/null")
            
            # Configure with CAN FD support
            cmd = (f"ip link set {self.interface} up type can "
                   f"bitrate {self.bitrate} "
                   f"dbitrate {self.data_bitrate} "
                   f"fd on")
            
            print(f"📝 Running: {cmd}")
            result = os.system(cmd)
            
            if result != 0:
                print(f"❌ Failed to configure {self.interface}")
                return False
            
            # Verify interface is UP
            time.sleep(0.5)
            status = os.popen(f"ip link show {self.interface}").read()
            
            if "state UP" in status:
                print(f"✅ {self.interface} configured successfully")
                print(f"   Arbitration bitrate: {self.bitrate}")
                print(f"   Data bitrate: {self.data_bitrate}")
                return True
            else:
                print(f"❌ {self.interface} failed to come UP")
                return False
                
        except Exception as e:
            print(f"❌ Interface setup error: {e}")
            return False
    
    def create_socket(self):
        """Create and configure CAN FD socket - like your create_canfd_socket()"""
        print(f"🔌 Creating CAN FD socket...")
        
        try:
            # Create CAN socket
            self.socket = socket.socket(socket.PF_CAN, socket.SOCK_RAW, socket.CAN_RAW)
            print("✅ CAN socket created")
            
            # Enable CAN FD frames
            self.socket.setsockopt(SOL_CAN_RAW, CAN_RAW_FD_FRAMES, 1)
            print("✅ CAN FD frames enabled")
            
            # Bind to interface
            self.socket.bind((self.interface,))
            print(f"✅ Socket bound to {self.interface}")
            
            return True
            
        except OSError as e:
            if e.errno == 22:  # EINVAL
                print("❌ CAN FD not supported on this interface")
            elif e.errno == 100:  # ENETDOWN
                print("❌ Network interface is down")
            else:
                print(f"❌ Socket error: {e}")
            return False
        except Exception as e:
            print(f"❌ Unexpected error: {e}")
            return False
    
    def set_filters(self, filters=None):
        """Set CAN filters - like your set_can_filters()"""
        if not filters:
            print("📡 No filters set - will receive ALL CAN frames")
            return True
            
        try:
            # Convert filters to binary format
            filter_data = b''
            for can_id, mask in filters:
                filter_data += struct.pack("II", can_id, mask)
            
            self.socket.setsockopt(SOL_CAN_RAW, CAN_RAW_FILTER, filter_data)
            print(f"✅ Set {len(filters)} CAN filters")
            for can_id, mask in filters:
                print(f"   Filter: ID=0x{can_id:03X}, Mask=0x{mask:03X}")
            return True
            
        except Exception as e:
            print(f"❌ Failed to set filters: {e}")
            return False
    
    def parse_frame(self, frame_data):
        """Parse received CAN FD frame - like your process_pdc3_can_frame()"""
        try:
            # Unpack frame header (first 8 bytes)
            can_id, dlc, flags, res0, res1 = struct.unpack("<IBBBB", frame_data[:8])
            
            # Extract data (up to 64 bytes)
            data = frame_data[8:8+dlc] if dlc > 0 else b''
            
            # Determine frame type
            is_extended = bool(can_id & CAN_EFF_FLAG)
            is_rtr = bool(can_id & CAN_RTR_FLAG)
            is_error = bool(can_id & CAN_ERR_FLAG)
            
            # Clean CAN ID (remove flags)
            clean_id = can_id & 0x1FFFFFFF if is_extended else can_id & 0x7FF
            
            # CAN FD specific flags
            is_fd = bool(flags & CANFD_FDF)
            has_brs = bool(flags & CANFD_BRS)
            has_esi = bool(flags & CANFD_ESI)
            
            return {
                'can_id': clean_id,
                'dlc': dlc,
                'data': data,
                'is_extended': is_extended,
                'is_rtr': is_rtr,
                'is_error': is_error,
                'is_fd': is_fd,
                'has_brs': has_brs,
                'has_esi': has_esi,
                'raw_id': can_id,
                'flags': flags
            }
            
        except Exception as e:
            print(f"❌ Frame parsing error: {e}")
            return None
    
    def format_frame_output(self, frame):
        """Format frame for display - educational output"""
        timestamp = datetime.now().strftime("%H:%M:%S.%f")[:-3]
        
        # Frame type
        frame_type = "CAN-FD" if frame['is_fd'] else "CAN2.0"
        if frame['is_error']:
            frame_type = "ERROR"
        elif frame['is_rtr']:
            frame_type += "-RTR"
        
        # ID format
        id_format = "EXT" if frame['is_extended'] else "STD"
        
        # Flags
        flags = []
        if frame['is_fd']:
            flags.append("FD")
        if frame['has_brs']:
            flags.append("BRS")
        if frame['has_esi']:
            flags.append("ESI")
        
        flag_str = f"[{','.join(flags)}]" if flags else ""
        
        # Data formatting
        data_hex = frame['data'].hex().upper()
        data_ascii = ''.join(chr(b) if 32 <= b < 127 else '.' for b in frame['data'])
        
        # Build output line
        output = f"{timestamp} {self.interface} {frame_type:>7} {id_format} "
        output += f"0x{frame['can_id']:03X} [{frame['dlc']:2d}] "
        output += f"{data_hex:<32} {flag_str:<12} '{data_ascii}'"
        
        return output
    
    def listen(self, max_frames=None, timeout=None):
        """Main listening loop - like your receive thread"""
        print(f"\n🎧 Starting CAN FD listener on {self.interface}...")
        print("📊 Frame format: TIMESTAMP INTERFACE TYPE FORMAT ID [DLC] DATA FLAGS 'ASCII'")
        print("-" * 100)
        
        self.running = True
        self.start_time = time.time()
        
        try:
            while self.running:
                # Set socket timeout if specified
                if timeout:
                    self.socket.settimeout(timeout)
                
                try:
                    # Receive frame (like your read() in receive thread)
                    frame_data = self.socket.recv(72)  # CAN FD frame size
                    
                    if len(frame_data) < 8:
                        print(f"⚠️  Incomplete frame received: {len(frame_data)} bytes")
                        continue
                    
                    # Parse frame
                    frame = self.parse_frame(frame_data)
                    if not frame:
                        continue
                    
                    # Update statistics
                    self.frame_count += 1
                    if frame['is_fd']:
                        self.canfd_frames += 1
                    elif frame['is_error']:
                        self.error_frames += 1
                    else:
                        self.can20_frames += 1
                    
                    # Display frame
                    print(self.format_frame_output(frame))
                    
                    # Check max frames limit
                    if max_frames and self.frame_count >= max_frames:
                        print(f"\n✅ Received {max_frames} frames, stopping...")
                        break
                        
                except socket.timeout:
                    print(f"\n⏰ Timeout: No frames received for {timeout} seconds")
                    break
                except KeyboardInterrupt:
                    print(f"\n\n🛑 Stopped by user")
                    break
                except Exception as e:
                    print(f"\n❌ Receive error: {e}")
                    break
                    
        finally:
            self.running = False
            self.print_statistics()
    
    def print_statistics(self):
        """Print reception statistics"""
        if self.start_time:
            duration = time.time() - self.start_time
            rate = self.frame_count / duration if duration > 0 else 0
            
            print("\n" + "=" * 60)
            print("📊 RECEPTION STATISTICS")
            print("=" * 60)
            print(f"Duration: {duration:.1f} seconds")
            print(f"Total frames: {self.frame_count}")
            print(f"CAN 2.0 frames: {self.can20_frames}")
            print(f"CAN FD frames: {self.canfd_frames}")
            print(f"Error frames: {self.error_frames}")
            print(f"Average rate: {rate:.1f} frames/second")
            print("=" * 60)
    
    def close(self):
        """Cleanup socket"""
        self.running = False
        if self.socket:
            self.socket.close()
            self.socket = None
            print(f"✅ Socket closed")

def signal_handler(sig, frame):
    """Handle Ctrl+C gracefully"""
    print("\n\n🛑 Shutting down...")
    sys.exit(0)

def main():
    import argparse
    
    parser = argparse.ArgumentParser(description='CAN FD Listener for i.MX8')
    parser.add_argument('-i', '--interface', default='can0',
                        help='CAN interface (default: can0)')
    parser.add_argument('-b', '--bitrate', type=int, default=500000,
                        help='Arbitration bitrate (default: 500000)')
    parser.add_argument('-d', '--dbitrate', type=int, default=2000000,
                        help='Data bitrate (default: 2000000)')
    parser.add_argument('-n', '--count', type=int,
                        help='Stop after N frames')
    parser.add_argument('-t', '--timeout', type=int,
                        help='Timeout in seconds')
    parser.add_argument('--filter', action='append', nargs=2, metavar=('ID', 'MASK'),
                        help='Add CAN filter: ID MASK (can use multiple times)')
    parser.add_argument('--no-setup', action='store_true',
                        help='Skip interface configuration')
    
    args = parser.parse_args()
    
    # Check if running as root
    if not args.no_setup and os.geteuid() != 0:
        print("❌ Run as root to configure CAN interface")
        print("   Or use --no-setup if interface is already configured")
        sys.exit(1)
    
    # Setup signal handler
    signal.signal(signal.SIGINT, signal_handler)
    
    # Create listener
    listener = CANFDListener(args.interface, args.bitrate, args.dbitrate)
    
    try:
        # Setup interface
        if not args.no_setup:
            if not listener.setup_interface():
                sys.exit(1)
        
        # Create socket
        if not listener.create_socket():
            sys.exit(1)
        
        # Setup filters if specified
        if args.filter:
            filters = []
            for id_str, mask_str in args.filter:
                can_id = int(id_str, 0)  # Auto-detect hex/decimal
                mask = int(mask_str, 0)
                filters.append((can_id, mask))
            listener.set_filters(filters)
        
        # Start listening
        listener.listen(max_frames=args.count, timeout=args.timeout)
        
    except Exception as e:
        print(f"❌ Error: {e}")
        sys.exit(1)
    
    finally:
        listener.close()

if __name__ == "__main__":
    main()
​
 
Thanks,
Abhay


Tags (2)
0 Kudos
Reply

214 Views
danielchen
NXP TechSupport
NXP TechSupport

Hi @abhayaugustin1 

 

CAN FD is supported in imx8mp can driver,  but I have difficulty  to test this feature with 8MP EVK board.

Now I am using imx93 EVK to test this feature, since i.mx93 and i.mx8mp share the same driver.  The python script works for i.MX93 EVK.   Do you have C script for CAN FD ?

danielchen_0-1754983506973.png

 

Regards

Daniel

 

0 Kudos
Reply

164 Views
danielchen
NXP TechSupport
NXP TechSupport

Hi @abhayaugustin1 

 

I tried  the setsockopt  function (C-program) on i.MX 8MP evk in my side, CAN FD socket configuration succeeds without errors.   

danielchen_2-1755761426778.png

 

danielchen_1-1755761351492.png

 

Regards

Daniel

0 Kudos
Reply