#include <stdio.h>
#include <string.h>
#include <unistd.h>
+#include <getopt.h>
#include <time.h>
#include <math.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <linux/can.h>
#include <pthread.h>
+#include "cegwerr.h"
+#include "readif.h"
/**
* ToDo:
* [ ] consider can timestamp
*/
+#define BENCH_FLAG_N 1
+#define BENCH_FLAG_SRC 2
+#define BENCH_FLAG_DST 4
+
+enum {
+ BENCH_MODE_UNDEF,
+ BENCH_MODE_ASAP,
+ BENCH_MODE_ONEATTIME,
+ BENCH_MODE_PERIOD
+};
+
+int can_sock_create( int ifindex );
+int udp_sock_create( struct in_addr ip, unsigned int port );
+
struct optdata
{
+ int optflag;
+ int mode;
int n;
+ struct cegw_if ceif[2]; /* 0 -> src, 1 -> dst */
};
struct optdata d;
pthread_barrier_t barrier;
struct timespec* tx_time, * rx_time;
-void* udp_recv( void* arg )
+void* thr_recv( void* arg )
{
- struct sockaddr_in sa_udp;
- int udp_sock;
+ int sock;
struct can_frame cf;
int i;
- sa_udp.sin_family = AF_INET;
- sa_udp.sin_port = htons( 10502 );
- inet_aton( "127.0.0.1", &sa_udp.sin_addr );
-
- udp_sock = socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP );
- if( udp_sock < 0 )
- {
- fprintf( stderr, "error: udp socket creation failed\n" );
+ if( d.ceif[1].type == IF_CAN )
+ sock = can_sock_create( d.ceif[1].can.ifindex );
+ else
+ sock = udp_sock_create( d.ceif[1].eth.ip, d.ceif[1].eth.port );
+ if( sock == -1 )
return NULL;
- }
-
- if( bind(udp_sock, (struct sockaddr*)&sa_udp, sizeof(sa_udp)) != 0 )
- {
- fprintf( stderr, "error: udp socket binding failed\n" );
- return NULL;
- }
pthread_barrier_wait( &barrier );
/* recv */
for( i=0; i<d.n; i++ )
{
- recvfrom( udp_sock, &cf, sizeof(cf), 0, NULL, 0 );
+ recvfrom( sock, &cf, sizeof(cf), 0, NULL, 0 );
clock_gettime( CLOCK_REALTIME, &rx_time[cf.data[0]] );
printf( "recv: (id=%d)%u\n", cf.can_id, cf.data[0] );
+ if( d.mode == BENCH_MODE_ONEATTIME )
+ {
+ pthread_barrier_wait( &barrier );
+ }
}
- close( udp_sock );
+ close( sock );
return NULL;
}
int nsec = (y->tv_nsec - x->tv_nsec) / 1000000000 + 1;
y->tv_nsec -= 1000000000 * nsec;
y->tv_sec += nsec;
- }
- if (x->tv_nsec - y->tv_nsec > 1000000000) {
+ }
+ if (x->tv_nsec - y->tv_nsec > 1000000000)
+ {
int nsec = (x->tv_nsec - y->tv_nsec) / 1000000000;
y->tv_nsec += 1000000000 * nsec;
y->tv_sec -= nsec;
return x->tv_sec < y->tv_sec;
}
+int udp_sock_create( struct in_addr ip, unsigned int port )
+{
+ struct sockaddr_in addr_udp;
+ int udp_sock;
+
+ addr_udp.sin_family = AF_INET;
+ addr_udp.sin_port = htons( port );
+ addr_udp.sin_addr.s_addr = INADDR_ANY;
+
+ udp_sock = socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP );
+ if( udp_sock < 0 )
+ {
+ fprintf( stderr, "error: udp socket creation failed\n" );
+ return -1;
+ }
+
+ if( bind(udp_sock, (struct sockaddr*)&addr_udp, sizeof(addr_udp)) != 0 )
+ {
+ fprintf( stderr, "error: udp socket binding failed\n" );
+ return -1;
+ }
+
+ return udp_sock;
+}
+
+/**
+ * can_sock_create()
+ * @return can socket fd, or -1 on failure
+ */
+int can_sock_create( int ifindex )
+{
+ int can_sock;
+ struct sockaddr_can addr_can;
+
+ addr_can.can_family = AF_CAN;
+ addr_can.can_ifindex = ifindex;
+
+ can_sock = socket( PF_CAN, SOCK_RAW, CAN_RAW );
+ if( can_sock < 0 )
+ {
+ fprintf( stderr, "error: can socket creation failed\n" );
+ return -1;
+ }
+
+ if( bind(can_sock, (struct sockaddr*)&addr_can, sizeof(addr_can)) != 0 )
+ {
+ fprintf( stderr, "error: can socket binding failed\n" );
+ return -1;
+ }
+
+ return can_sock;
+}
+
+int read_mode( char* in )
+{
+ if( strcmp( in, "asap" ) == 0 )
+ return BENCH_MODE_ASAP;
+ if( strcmp( in, "oneattime") == 0 )
+ return BENCH_MODE_ONEATTIME;
+ if( strcmp( in, "period" ) == 0 )
+ return BENCH_MODE_PERIOD;
+
+ return BENCH_MODE_UNDEF;
+}
+
int main( int argc, char* argv[] )
{
char opt;
- struct sockaddr_can sa_can;
struct can_frame cf;
- int can_sock;
+ int sock;
pthread_t thr;
float mean = 0;
float time;
+ int ret;
struct timespec res;
- int i;
+ int i, tmp;
+
+ d.optflag = 0;
+ d.mode = BENCH_MODE_ASAP;
+
+ struct option longopt[] =
+ {
+ { "mode", 0, NULL, 'm' },
+ { 0, 0, 0, 0 }
+ };
while( 1 )
{
- opt = getopt( argc, argv, "n:" );
+ opt = getopt( argc, argv, "s:d:m:n:" );
if( opt == -1 )
break;
switch( opt )
{
+ case 's':
+ d.optflag |= BENCH_FLAG_SRC;
+ if( read_if( optarg, &d.ceif[0] ) != 0 )
+ {
+ perr( "'-s'" );
+ return -1;
+ }
+ break;
+ case 'd':
+ d.optflag |= BENCH_FLAG_DST;
+ if( read_if( optarg, &d.ceif[1] ) )
+ {
+ perr( "-d" );
+ return -1;
+ }
+ break;
+ case 'm':
+ tmp = read_mode( optarg );
+ if( tmp == BENCH_MODE_UNDEF )
+ {
+ perr( "benchmarking mode unknown" );
+ return -1;
+ }
+ d.mode = tmp;
+ break;
case 'n':
+ d.optflag |= BENCH_FLAG_N;
d.n = atoi( optarg );
break;
case '?':
}
}
+ /* check opt */
+ /* '-n' option is mandatory and its value is >0 */
+ if( d.optflag & BENCH_FLAG_N )
+ {
+ if( d.n < 1 )
+ {
+ perr( "'-n' should be at least 1." );
+ }
+ } else
+ {
+ perr( "'-n' is mandatory." );
+ return -1;
+ }
+
+ /* '-s' and '-d' should be defined and different iftype */
+ if( (d.optflag & BENCH_FLAG_SRC) && (d.optflag & BENCH_FLAG_DST) )
+ {
+ if( !(d.ceif[0].type == IF_CAN && d.ceif[1].type == IF_ETH_UDP) &&
+ !(d.ceif[0].type == IF_ETH_UDP && d.ceif[1].type == IF_CAN ) )
+ {
+ perr( "'-s' and '-d' should be different interface type." );
+ return -1;
+ }
+ } else
+ {
+ perr( "'-s' and '-d' should be defined." );
+ return -1;
+ }
+
tx_time = malloc( d.n*sizeof(struct timespec) );
rx_time = malloc( d.n*sizeof(struct timespec) );
pthread_barrier_init( &barrier, NULL, 2 );
- pthread_create( &thr, NULL, udp_recv, NULL );
+ pthread_create( &thr, NULL, thr_recv, NULL );
- sa_can.can_family = AF_CAN;
- sa_can.can_ifindex = if_nametoindex( "vcan0" );
-
- can_sock = socket( PF_CAN, SOCK_RAW, CAN_RAW );
- if( can_sock < 0 )
+ /**/
+ struct sockaddr* addr;
+ struct sockaddr_can addr_can;
+ struct sockaddr_in addr_udp;
+ int addr_size;
+
+ if( d.ceif[0].type == IF_CAN )
{
- fprintf( stderr, "error: can socket creation failed\n" );
- return -1;
+ sock = socket( PF_CAN, SOCK_RAW, CAN_RAW );
+ addr_can.can_family = AF_CAN;
+ addr_can.can_ifindex = d.ceif[0].can.ifindex;
+ addr = (struct sockaddr*)&addr_can;
+ addr_size = sizeof( addr_can );
}
-
- if( bind(can_sock, (struct sockaddr*)&sa_can, sizeof(sa_can)) != 0 )
+ else
{
- fprintf( stderr, "error: can socket binding failed\n" );
- return -1;
+ sock = socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP );
+ addr_udp.sin_family = AF_INET;
+ addr_udp.sin_addr = d.ceif[0].eth.ip;
+ addr_udp.sin_port = htons( d.ceif[0].eth.port );
+ addr = (struct sockaddr*)&addr_udp;
+ addr_size = sizeof( addr_udp );
}
+ if( sock < 0 ) /* ToDo: ? */
+ return -1;
pthread_barrier_wait( &barrier );
/* send */
for( i=0; i<d.n; i++ )
{
- //memset( &cf, 0, sizeof(cf) );
cf.can_id = i;
cf.can_dlc = 8;
cf.data[0] = i;
- int ret = sendto( can_sock, &cf, sizeof(cf), 0, (struct sockaddr*)&sa_can, sizeof(sa_can) );
+
clock_gettime( CLOCK_REALTIME, &tx_time[cf.data[0]] );
+ ret = sendto( sock, &cf, sizeof(cf), 0, addr, addr_size );
printf( "sending(%d)\n", ret );
+ if( d.mode == BENCH_MODE_ONEATTIME )
+ {
+ pthread_barrier_wait( &barrier );
+ }
}
- close( can_sock ); /* shutdown? */
+ close( sock ); /* ToDo: shutdown? */
pthread_join( thr, NULL );
/* results */
+ int tn = 0;
for( i=0; i<d.n; i++ )
{
if( timespec_subtract( &res, &rx_time[i], &tx_time[i] ) == 0 )
{
time = res.tv_sec*1000000000.0f + (float)res.tv_nsec;
mean += time;
+ tn++;
printf( "transfer time:%fns\n", time );
} else
{
printf( "transfer time: irrelevant\n" );
}
}
- mean /= ++i;
+ mean /= tn; /* ToDo: did they arrive all? */
printf( "average: %fns\n", mean );
+ pthread_barrier_destroy( &barrier );
free( tx_time );
free( rx_time );
-// clock_gettime( CLOCK_REALTIME, &tx_time );
return 0;
}
--- /dev/null
+/*
+ * cesend.c - simple command line tool to send CAN-frames via udp
+ * cesend is a fork of cansend from Socket-CAN project.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+
+#include <linux/can.h>
+#include <linux/can/raw.h>
+
+#define CANID_DELIM '#'
+#define DATA_SEPERATOR '.'
+
+unsigned char asc2nibble(char c) {
+
+ if ((c >= '0') && (c <= '9'))
+ return c - '0';
+
+ if ((c >= 'A') && (c <= 'F'))
+ return c - 'A' + 10;
+
+ if ((c >= 'a') && (c <= 'f'))
+ return c - 'a' + 10;
+
+ return 16; /* error */
+}
+
+int parse_canframe(char *cs, struct can_frame *cf) {
+ /* documentation see lib.h */
+
+ int i, idx, dlc, len;
+ unsigned char tmp;
+
+ len = strlen(cs);
+ //printf("'%s' len %d\n", cs, len);
+
+ memset(cf, 0, sizeof(*cf)); /* init CAN frame, e.g. DLC = 0 */
+
+ if (len < 4)
+ return 1;
+
+ if (cs[3] == CANID_DELIM) { /* 3 digits */
+
+ idx = 4;
+ for (i=0; i<3; i++){
+ if ((tmp = asc2nibble(cs[i])) > 0x0F)
+ return 1;
+ cf->can_id |= (tmp << (2-i)*4);
+ }
+
+ } else if (cs[8] == CANID_DELIM) { /* 8 digits */
+
+ idx = 9;
+ for (i=0; i<8; i++){
+ if ((tmp = asc2nibble(cs[i])) > 0x0F)
+ return 1;
+ cf->can_id |= (tmp << (7-i)*4);
+ }
+ if (!(cf->can_id & CAN_ERR_FLAG)) /* 8 digits but no errorframe? */
+ cf->can_id |= CAN_EFF_FLAG; /* then it is an extended frame */
+
+ } else
+ return 1;
+
+ if((cs[idx] == 'R') || (cs[idx] == 'r')){ /* RTR frame */
+ cf->can_id |= CAN_RTR_FLAG;
+ return 0;
+ }
+
+ for (i=0, dlc=0; i<8; i++){
+
+ if(cs[idx] == DATA_SEPERATOR) /* skip (optional) seperator */
+ idx++;
+
+ if(idx >= len) /* end of string => end of data */
+ break;
+
+ if ((tmp = asc2nibble(cs[idx++])) > 0x0F)
+ return 1;
+ cf->data[i] = (tmp << 4);
+ if ((tmp = asc2nibble(cs[idx++])) > 0x0F)
+ return 1;
+ cf->data[i] |= tmp;
+ dlc++;
+ }
+
+ cf->can_dlc = dlc;
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ int s; /* can raw socket */
+ int nbytes;
+ struct sockaddr_in addr;
+ struct can_frame frame;
+
+ /* check command line options */
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s <can_frame>.\n", argv[0]);
+ return 1;
+ }
+
+ /* parse CAN frame */
+ if (parse_canframe(argv[1], &frame)){
+ fprintf(stderr, "\nWrong CAN-frame format!\n\n");
+ fprintf(stderr, "Try: <can_id>#{R|data}\n");
+ fprintf(stderr, "can_id can have 3 (SFF) or 8 (EFF) hex chars\n");
+ fprintf(stderr, "data has 0 to 8 hex-values that can (optionally)");
+ fprintf(stderr, " be seperated by '.'\n\n");
+ fprintf(stderr, "e.g. 5A1#11.2233.44556677.88 / 123#DEADBEEF / ");
+ fprintf(stderr, "5AA# /\n 1F334455#1122334455667788 / 123#R ");
+ fprintf(stderr, "for remote transmission request.\n\n");
+ return 1;
+ }
+
+ /* open socket */
+ if(( s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0 ) {
+ perror("socket");
+ return 1;
+ }
+
+ addr.sin_family = AF_INET;
+ inet_aton( "127.0.0.1", &addr.sin_addr );
+ addr.sin_port = htons( 10501 );
+
+ /* send frame */
+ if ((nbytes = sendto(s, &frame, sizeof(frame), 0, (struct sockaddr*)&addr, sizeof(addr))) != sizeof(frame)) {
+ perror("write");
+ return 1;
+ }
+
+ close(s);
+
+ return 0;
+}
+
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+#include "cegwerr.h"
+#include "readif.h"
+
+/**
+ * read_addrport - parses @in for eth address.
+ * Valid input is e.g. udp@127.0.0.1:10502 or can@vcan0.
+ *
+ * @param[in] in string to search in
+ * @param[out] addr ip address
+ * @param[out] port transport layer port
+ * @return 0 on success, -1 otherwise
+ */
+int read_addrport( char* in, struct in_addr* addr, unsigned short* port )
+{
+ char* delim = NULL;
+ char addrstr[16];
+ int addrlen;
+
+ if( (delim = strchr( in, ':' )) == NULL )
+ {
+ cegw_errno = CEGW_ERR_COLON;
+ return -1;
+ }
+
+ /* get address */
+ addrlen = delim - in;
+ memcpy( addrstr, in, addrlen );
+ addrstr[addrlen] = '\0';
+ if( inet_aton( addrstr, addr ) == 0 )
+ {
+ cegw_errno = CEGW_ERR_ATON;
+ return -1;
+ }
+
+ /* get port */
+ if( sscanf( delim, ":%hu", port ) != 1 ) /* ToDo: handle overflow */
+ {
+ cegw_errno = CEGW_ERR_PORT;
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * read_iftype - reads @in for iftype
+ * Iftype type is e.g. "can@" or "udp@".
+ *
+ * @param[in] in string to search in
+ * @param[out] iftype iftype detected
+ * @return pointer to @in after iftype on success, NULL otherwise
+ */
+char* read_iftype( char* in, int* iftype )
+{
+ char* ret = in+4;
+
+ if( strncmp( "udp@", optarg, 4 ) == 0 )
+ {
+ *iftype = IF_ETH_UDP;
+ return ret;
+ }
+ /*
+ if( strncmp( "tcp@", optarg, 4 ) == 0 )
+ {
+ return NULL;
+ }
+ */
+ if( strncmp( "can@", optarg, 4 ) == 0 )
+ {
+ *iftype = IF_CAN;
+ return ret;
+ }
+
+ cegw_errno = CEGW_ERR_IF_TYPE;
+ return NULL;
+}
+
+/**
+ * read_if - reads interface from @in
+ * Function analyzes @in for interface specification in format
+ * <if>@<ip>:<port>, where <if> is can or udp, <ip> is address in dotted
+ * format.
+ *
+ * @param[in] in string to search in
+ * @param[out] iftype interface type (IF_CAN or IF_ETH_UDP)
+ * @param[out] d ip and port is stored to @d
+ */
+
+/* ToDo: generalize
+int read_if( char* in, int* iftype, struct cegw_data* d )
+{
+ char* optstr = NULL;
+
+ if( (optstr = read_iftype( in, iftype )) == NULL )
+ {
+ return -1;
+ }
+
+ switch( *iftype )
+ {
+ case IF_CAN:
+ d->can_ifidx = if_nametoindex( optstr );
+ if( d->can_ifidx == 0 )
+ {
+ cegw_errno = CEGW_ERR_IF_CAN;
+ return -1;
+ }
+ break;
+ case IF_ETH_UDP:
+ if( read_addrport( optstr, &d->eth_addr, &d->eth_port ) != 0 )
+ {
+ return -1;
+ }
+ break;
+ default:
+ return -1;
+ break;
+ }
+
+ return 0;
+}
+*/
+
+int read_if( char* in, struct cegw_if* ceif )
+{
+ char* optstr = NULL;
+
+ if( (optstr = read_iftype( in, &ceif->type )) == NULL )
+ {
+ return -1;
+ }
+
+ switch( ceif->type )
+ {
+ case IF_CAN:
+ ceif->can.ifindex = if_nametoindex( optstr );
+ if( ceif->can.ifindex == 0 )
+ {
+ cegw_errno = CEGW_ERR_IF_CAN;
+ return -1;
+ }
+ break;
+ case IF_ETH_UDP:
+ if( read_addrport( optstr, &ceif->eth.ip, &ceif->eth.port ) != 0 )
+ {
+ return -1;
+ }
+ break;
+ default:
+ return -1;
+ break;
+ }
+
+ return 0;
+}