/*  Copyright (C) MOXA Inc. All rights reserved.

    This software is distributed under the terms of the
    MOXA License.  See the file COPYING-MOXA for details.
*/

/*
	tx_fd.c

	SocketCAN transmit example for Classical CAN and CAN FD

    2025-12-09	Eric XH Lin
		new release
*/

#include <errno.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <net/if.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#define DEFAULT_IFACE	"can0"
#define CLASSIC_ID	0x123
#define CLASSIC_DLC	8
#define FD_ID		0x1ABCDE
#define FD_LEN		16

int	main( int argc, char *argv[] )
{
	int s = -1;
	struct sockaddr_can addr;
	struct ifreq ifr;
	const char *ifname = ( argc > 1 ) ? argv[1] : DEFAULT_IFACE;
	int ret;
	int enable_canfd = 1;

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

	if( setsockopt( s, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &enable_canfd, sizeof(enable_canfd) ) < 0 ){
		perror( "setsockopt CAN_RAW_FD_FRAMES" );
		close( s );
		return -1;
	}

	addr.can_family = PF_CAN;
	memset( &ifr, 0, sizeof(ifr) );
	strncpy( ifr.ifr_name, ifname, IFNAMSIZ - 1 );

	if( ioctl( s, SIOCGIFINDEX, &ifr ) ){
		perror( "ioctl" );
		close( s );
		return -1;
	}

	addr.can_ifindex = ifr.ifr_ifindex;

	if( bind( s, (struct sockaddr *)&addr, sizeof(addr) ) < 0 ){
		perror( "bind" );
		close( s );
		return -1;
	}

	printf( "Using interface %s\n", ifname );

	/* Classical CAN frame */
	{
		struct can_frame cf = {0};
		int i;

		cf.can_id = CLASSIC_ID & CAN_SFF_MASK;
		cf.can_dlc = CLASSIC_DLC;
		for( i = 0; i < cf.can_dlc; i++ )
			cf.data[i] = 0x10 + i;

		ret = write( s, &cf, CAN_MTU );
		if( ret != CAN_MTU ){
			perror( "write classical" );
			close( s );
			return -1;
		}
		printf( "TX Classic: id=0x%03x dlc=%d\n", cf.can_id, cf.can_dlc );
	}

	/* CAN FD frame with BRS */
	{
		struct canfd_frame fdf = {0};
		int i;

		fdf.can_id = ( FD_ID & CAN_EFF_MASK ) | CAN_EFF_FLAG;
		fdf.len = FD_LEN;
		fdf.flags = CANFD_BRS;
		for( i = 0; i < fdf.len; i++ )
			fdf.data[i] = 0xA0 + i;

		ret = write( s, &fdf, CANFD_MTU );
		if( ret != CANFD_MTU ){
			if( errno == EINVAL ){
				printf( "TX FD skipped: interface not CAN FD capable or not up with fd on (enable via 'ip link set %s type can bitrate <bitrate> dbitrate <dbitrate> fd on')\n", ifname );
			} else {
				perror( "write FD" );
				close( s );
				return -1;
			}
		} else {
			printf( "TX FD: id=0x%08x len=%d flags=0x%x (BRS set)\n", fdf.can_id & CAN_EFF_MASK, fdf.len, fdf.flags );
		}
	}

	close( s );
	return 0;
}
