Merge branch 'master' of https://github.com/kurt-vd/test-can-j1939
commit
8839679a4c
|
|
@ -48,6 +48,7 @@ GNUmakefile.in
|
|||
/slcan_attach
|
||||
/slcand
|
||||
/slcanpty
|
||||
/testj1939
|
||||
|
||||
/can-utils-*.tar.bz2
|
||||
/can-utils-*.tar.gz
|
||||
|
|
|
|||
|
|
@ -63,7 +63,8 @@ bin_PROGRAMS = \
|
|||
log2long \
|
||||
slcan_attach \
|
||||
slcand \
|
||||
slcanpty
|
||||
slcanpty \
|
||||
testj1939
|
||||
|
||||
jacd_LDADD = libj1939.la
|
||||
jspy_LDADD = libj1939.la
|
||||
|
|
@ -73,7 +74,9 @@ EXTRA_DIST = \
|
|||
.travis.yml \
|
||||
Android.mk \
|
||||
README.md \
|
||||
autogen.sh
|
||||
autogen.sh \
|
||||
can-j1939-kickstart.md
|
||||
can-j1939.md
|
||||
|
||||
MAINTAINERCLEANFILES = \
|
||||
configure \
|
||||
|
|
|
|||
2
Makefile
2
Makefile
|
|
@ -55,7 +55,7 @@ CPPFLAGS += -Iinclude \
|
|||
PROGRAMS_ISOTP = isotpdump isotprecv isotpsend isotpsniffer isotptun isotpserver isotpperf
|
||||
PROGRAMS_CANGW = cangw
|
||||
PROGRAMS_SLCAN = slcan_attach slcand
|
||||
PROGRAMS_J1939 = jacd jspy jsr
|
||||
PROGRAMS_J1939 = jacd jspy jsr testj1939
|
||||
PROGRAMS = can-calc-bit-timing candump cansniffer cansend canplayer cangen canbusload\
|
||||
log2long log2asc asc2log\
|
||||
canlogserver bcmserver\
|
||||
|
|
|
|||
|
|
@ -0,0 +1,216 @@
|
|||
# Kickstart guide to can-j1939 on linux
|
||||
|
||||
## Prepare using VCAN
|
||||
|
||||
You may skip this step entirely if you have a functional
|
||||
**can0** bus on your system.
|
||||
|
||||
Load module, when *vcan* is not in-kernel
|
||||
|
||||
modprobe vcan
|
||||
|
||||
Create a virtual can0 device and start the device
|
||||
|
||||
ip link add can0 type vcan
|
||||
ip link set can0 up
|
||||
|
||||
## First steps with j1939
|
||||
|
||||
Use [testj1939](testj1939.c)
|
||||
|
||||
When *can-j1939* is compiled as module, opening a socket will load it,
|
||||
__or__ you can load it manually
|
||||
|
||||
modprobe can-j1939
|
||||
|
||||
Most of the subsequent examples will use 2 sockets programs (in 2 terminals).
|
||||
One will use CAN_J1939 sockets using *testj1939*,
|
||||
and the other will use CAN_RAW sockets using cansend+candump.
|
||||
|
||||
testj1939 can be told to print the used API calls by adding **-v** program argument.
|
||||
|
||||
### receive without source address
|
||||
|
||||
Do in terminal 1
|
||||
|
||||
./testj1939 -r can0:
|
||||
|
||||
Send raw CAN in terminal 2
|
||||
|
||||
cansend can0 1823ff40#0123
|
||||
|
||||
You should have this output in terminal 1
|
||||
|
||||
40 02300: 01 23
|
||||
|
||||
This means, from NAME 0, SA 40, PGN 02300 was received,
|
||||
with 2 databytes, *01* & *02*.
|
||||
|
||||
now emit this CAN message:
|
||||
|
||||
cansend can0 18234140#0123
|
||||
|
||||
In J1939, this means that ECU 0x40 sends directly to ECU 0x41
|
||||
Since we did not bind to address 0x41, this traffic
|
||||
is not meant for us and *testj1939* does not receive it.
|
||||
|
||||
### Use source address
|
||||
|
||||
Binding a can-j1939 socket to a source address will register
|
||||
allow you to send packets.
|
||||
|
||||
./testj1939 can0:0x80
|
||||
|
||||
Your system had, for a small moment, source address 0x80 assigned.
|
||||
|
||||
### receive with source address
|
||||
|
||||
Terminal 1:
|
||||
|
||||
./testj1939 -r can0:0x80
|
||||
|
||||
Terminal 2:
|
||||
|
||||
cansend can0 18238040#0123
|
||||
|
||||
Will emit this output
|
||||
|
||||
40 02300: 01 23
|
||||
|
||||
This is because the traffic had destination address __0x80__ .
|
||||
|
||||
### send
|
||||
|
||||
Open in terminal 1:
|
||||
|
||||
candump -L can0
|
||||
|
||||
And to these test in another terminal
|
||||
|
||||
./testj1939 -s can0:0x80,0x3ffff
|
||||
|
||||
This produces **1BFFFF80#0123456789ABCDEF** on CAN.
|
||||
|
||||
### Multiple source addresses on 1 CAN device
|
||||
|
||||
./testj1939 -s can0:0x90,0x3ffff
|
||||
|
||||
produces **1BFFFF90#0123456789ABCDEF** ,
|
||||
|
||||
### Use PDU1 PGN
|
||||
|
||||
./testj1939 -s can0:0x80,0x12345
|
||||
|
||||
emits **1923FF80#0123456789ABCDEF** .
|
||||
|
||||
Note that the real PGN is **0x12300**, and destination address is **0xff**.
|
||||
|
||||
### Use destination address info
|
||||
|
||||
The destination field may be set during sendto().
|
||||
*testj1939* implements that like this
|
||||
|
||||
./testj1939 -s can0:0x80,0x12345 can0:0x40
|
||||
|
||||
emits **19234080#0123456789ABCDEF** .
|
||||
|
||||
The destination CAN iface __must__ always match the source CAN iface.
|
||||
Specifing one during bind is therefore sufficient.
|
||||
|
||||
./testj1939 -s can0:,0x12300 :0x40
|
||||
|
||||
emits the very same.
|
||||
|
||||
### Emit different PGNs using the same socket
|
||||
|
||||
The PGN is provided in both __bind( *sockname* )__ and
|
||||
__sendto( *peername* )__ , and only one is used.
|
||||
*peername* PGN has highest precedence.
|
||||
|
||||
For broadcasted transmissions
|
||||
|
||||
./testj1939 -s can0:0x80,0x12300 :,0x32100
|
||||
|
||||
emits **1B21FF80#0123456789ABCDEF** rather than 1923FF80#012345678ABCDEF
|
||||
|
||||
Desitination specific transmissions
|
||||
|
||||
./testj1939 -s can0:0x80,0x12300 :0x40,0x32100
|
||||
|
||||
emits **1B214080#0123456789ABCDEF** .
|
||||
|
||||
It makes sometimes sense to omit the PGN in __bind( *sockname* )__ .
|
||||
|
||||
### Larger packets
|
||||
|
||||
J1939 transparently switches to *Transport Protocol* when packets
|
||||
do not fit into single CAN packets.
|
||||
|
||||
./testj1939 -s20 can0:0x80 :,0x12300
|
||||
|
||||
emits:
|
||||
|
||||
18ECFF80#20140003FF002301
|
||||
|
||||
This is the first fragment for broadcasted *Transport Protocol*.
|
||||
_testj1939_ returns before the subsequent packets can leave, and
|
||||
as the last socket on the system closes, can-j1939 effectively
|
||||
cleans up all resources. Real-world applications will run like forever,
|
||||
and will not encounter this side-effect.
|
||||
|
||||
Try again, and instruct _testj1939_ to keep the socket open for 1 second.
|
||||
|
||||
./testj1939 -w1.0 -s20 can0:0x80 :,0x12300
|
||||
|
||||
emits:
|
||||
|
||||
18ECFF80#20140003FF002301
|
||||
18EBFF80#010123456789ABCD
|
||||
18EBFF80#02EF0123456789AB
|
||||
18EBFF80#03CDEF01234567
|
||||
|
||||
The fragments for broadcasted *Transport Protocol* are seperated
|
||||
__50ms__ from each other.
|
||||
Destination specific *Transport Protocol* applies flow control
|
||||
and may emit CAN packets much faster.
|
||||
|
||||
First assign 0x90 to the local system.
|
||||
This becomes important because the kernel must interact in the
|
||||
transport protocol sessions before the complete packet is delivered.
|
||||
|
||||
./testj1939 can0:0x90 -r &
|
||||
|
||||
Now test:
|
||||
|
||||
./testj1939 -s20 can0:0x80 :0x90,0x12300
|
||||
|
||||
emits:
|
||||
|
||||
18EC9080#1014000303002301
|
||||
18EC8090#110301FFFF002301
|
||||
18EB9080#010123456789ABCD
|
||||
18EB9080#02EF0123456789AB
|
||||
18EB9080#03CDEF01234567
|
||||
18EC8090#13140003FF002301
|
||||
|
||||
The flow control causes a bit overhead.
|
||||
This overhead scales very good for larger J1939 packets.
|
||||
|
||||
## Advanced topics with j1939
|
||||
|
||||
### Change priority of J1939 packets
|
||||
|
||||
./testj1939 -s can0:0x80,0x0100
|
||||
./testj1939 -s -p3 can0:0x80,0x0200
|
||||
|
||||
emits
|
||||
|
||||
1801FF80#0123456789ABCDEF
|
||||
0C02FF80#0123456789ABCDEF
|
||||
|
||||
### using connect
|
||||
|
||||
### advanced filtering
|
||||
|
||||
## dynamic addressing
|
||||
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
# CAN-J1939 on linux
|
||||
|
||||
The [Kickstart guide is here](can-j1939-kickstart.md)
|
||||
|
||||
## CAN on linux
|
||||
|
||||
See [Wikipedia:socketcan](http://en.wikipedia.org/wiki/Socketcan)
|
||||
|
||||
## J1939 networking in short
|
||||
|
||||
* Add addressing on top of CAN (destination address & broadcast)
|
||||
|
||||
* Any (max 1780) length packets.
|
||||
Packets of 9 or more use **Transport Protocol** (fragmentation)
|
||||
Such packets use different CANid for the same PGN.
|
||||
|
||||
* only **29**bit, non-**RTR** CAN frames
|
||||
|
||||
* CAN id is composed of
|
||||
* 0..8: SA (source address)
|
||||
* 9..26:
|
||||
* PDU1: PGN+DA (destionation address)
|
||||
* PDU2: PGN
|
||||
* 27..29: PRIO
|
||||
|
||||
* SA / DA may be dynamically assigned via j1939-81
|
||||
Fixed rules of precedence in Specification, no master necessary
|
||||
|
||||
## J1939 on SocketCAN
|
||||
|
||||
J1939 is *just another protocol* that fits
|
||||
in the Berkely sockets.
|
||||
|
||||
socket(AF_CAN, SOCK_DGRAM, CAN_J1939)
|
||||
|
||||
## differences from CAN_RAW
|
||||
### addressing
|
||||
|
||||
SA, DA & PGN are used, not CAN id.
|
||||
|
||||
Berkeley socket API is used to communicate these to userspace:
|
||||
|
||||
* SA+PGN is put in sockname ([getsockname](http://man7.org/linux/man-pages/man2/getsockname.2.html))
|
||||
* DA+PGN is put in peername ([getpeername](http://man7.org/linux/man-pages/man2/getpeername.2.html))
|
||||
PGN is put in both structs
|
||||
|
||||
PRIO is a datalink property, and irrelevant for interpretation
|
||||
Therefore, PRIO is not in *sockname* or *peername*.
|
||||
|
||||
The *data* that is [recv][recvfrom] or [send][sendto] is the real payload.
|
||||
Unlike CAN_RAW, where addressing info is data.
|
||||
|
||||
### Packet size
|
||||
|
||||
J1939 handles packets of 8+ bytes with **Transport Protocol** fragmentation transparently.
|
||||
No fixed data size is necessary.
|
||||
|
||||
send(sock, data, 8, 0);
|
||||
|
||||
will emit a single CAN frame.
|
||||
|
||||
send(sock, data, 9, 0);
|
||||
|
||||
will use fragementation, emitting 1+ CAN frames.
|
||||
|
||||
## Enable j1939 (obsolete!)
|
||||
|
||||
CAN has no protocol id field.
|
||||
The can-j1939 stack only activates when a socket opens
|
||||
for a network device.
|
||||
|
||||
The methods described here existed in earlier implementations.
|
||||
|
||||
### netlink
|
||||
|
||||
ip link set can0 j1939 on
|
||||
|
||||
This method is obsoleted in favor of _on socket connect_.
|
||||
|
||||
### procfs for legacy kernel (2.6.25)
|
||||
|
||||
This API is dropped for kernels with netlink support!
|
||||
|
||||
echo can0 > /proc/net/can-j1939/net
|
||||
|
||||
# Using J1939
|
||||
|
||||
## BSD socket implementation
|
||||
* socket
|
||||
* bind / connect
|
||||
* recvfrom / sendto
|
||||
* getsockname / getpeername
|
||||
|
||||
## Modified *struct sockaddr_can*
|
||||
|
||||
struct sockaddr_can {
|
||||
sa_family_t can_family;
|
||||
int can_ifindex;
|
||||
union {
|
||||
struct {
|
||||
__u64 name;
|
||||
__u32 pgn;
|
||||
__u8 addr;
|
||||
} j1939;
|
||||
} can_addr;
|
||||
}
|
||||
|
||||
* *can_addr.j1939.pgn* is PGN
|
||||
|
||||
* *can_addr.j1939.addr* & *can_addr.j1939.name*
|
||||
determine the ECU
|
||||
|
||||
* receiving address information,
|
||||
*addr* is always set,
|
||||
*name* is set when available.
|
||||
|
||||
* When providing address information,
|
||||
*name* != 0 indicates dynamic addressing
|
||||
|
||||
## iproute2 (obsolete!)
|
||||
|
||||
Older versions of can-j1939 used a modified iproute2
|
||||
for manipulating the kernel lists of current addresses.
|
||||
|
||||
### Static addressing
|
||||
|
||||
ip addr add j1939 0x80 dev can0
|
||||
|
||||
### Dynamic addressing
|
||||
|
||||
ip addr add j1939 name 0x012345678abcdef dev can0
|
||||
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>page: <?theme title?></title>
|
||||
<link rel="stylesheet" type="text/css" href="style.css">
|
||||
<style>
|
||||
|
||||
div#toc li {
|
||||
list-style : none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id='toc'>
|
||||
<?theme toc?>
|
||||
</div>
|
||||
<?theme body?>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
* {
|
||||
font-family: Helvetica Neue, Helvetica, Arial, sans-serif;
|
||||
}
|
||||
body {
|
||||
max-width: 60em;
|
||||
margin: 0 auto;
|
||||
color: #111;
|
||||
}
|
||||
|
||||
pre, code {
|
||||
font-family: Monaco, Courier New, monospace;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: rgb(43,105,145);
|
||||
font-weight: bold;
|
||||
font-size: 40px;
|
||||
letter-spacing: -1px;
|
||||
margin-bottom: -5px;
|
||||
margin: 0;
|
||||
}
|
||||
h1 code {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: rgb(43,105,145);
|
||||
font-weight: bold;
|
||||
margin-bottom: -5px;
|
||||
}
|
||||
h2 code {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-bottom: -5px;
|
||||
}
|
||||
h3 code {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: blue;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:visited {
|
||||
color: navy;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
pre {
|
||||
border-width: 1px;
|
||||
border-color: #777;
|
||||
border-style: solid;
|
||||
padding: 0.5em;
|
||||
background-color: #ccc;
|
||||
overflow: auto;
|
||||
color: #000;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
p, li {
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
|
@ -0,0 +1,290 @@
|
|||
/*
|
||||
* Copyright (c) 2013 EIA Electronics
|
||||
*
|
||||
* Authors:
|
||||
* Kurt Van Dijck <kurt.van.dijck@eia.be>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the version 2 of the GNU General Public License
|
||||
* as published by the Free Software Foundation
|
||||
*/
|
||||
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include <inttypes.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <error.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/socket.h>
|
||||
#include <net/if.h>
|
||||
#include <linux/can.h>
|
||||
#include <linux/can/j1939.h>
|
||||
|
||||
static const char help_msg[] =
|
||||
"testj1939: demonstrate j1939 use\n"
|
||||
"Usage: testj1939 FROM TO\n"
|
||||
" FROM / TO - or [IFACE][:[SA][,[PGN][,NAME]]]\n"
|
||||
"Options:\n"
|
||||
" -v Print relevant API calls\n"
|
||||
" -s[=LEN] Initial send of LEN bytes dummy data\n"
|
||||
" -r Receive (and print) data\n"
|
||||
" -e Echo incoming packets back\n"
|
||||
" This atually receives packets\n"
|
||||
" -c Issue connect()\n"
|
||||
" -p=PRIO Set priority to PRIO\n"
|
||||
" -n Emit 64bit NAMEs in output\n"
|
||||
" -w[TIME] Return after TIME (default 1) seconds\n"
|
||||
"\n"
|
||||
"Example:\n"
|
||||
"testj1939 can1 20\n"
|
||||
"\n"
|
||||
;
|
||||
|
||||
static const char optstring[] = "?vs::rep:cnw::";
|
||||
|
||||
static void parse_canaddr(char *spec, struct sockaddr_can *paddr)
|
||||
{
|
||||
char *str;
|
||||
|
||||
str = strsep(&spec, ":");
|
||||
if (strlen(str))
|
||||
paddr->can_ifindex = if_nametoindex(str);
|
||||
|
||||
str = strsep(&spec, ",");
|
||||
if (str && strlen(str))
|
||||
paddr->can_addr.j1939.addr = strtoul(str, NULL, 0);
|
||||
|
||||
str = strsep(&spec, ",");
|
||||
if (str && strlen(str))
|
||||
paddr->can_addr.j1939.pgn = strtoul(str, NULL, 0);
|
||||
|
||||
str = strsep(&spec, ",");
|
||||
if (str && strlen(str))
|
||||
paddr->can_addr.j1939.name = strtoul(str, NULL, 0);
|
||||
}
|
||||
|
||||
static const char *canaddr2str(const struct sockaddr_can *paddr)
|
||||
{
|
||||
static char buf[128];
|
||||
char *str = buf;
|
||||
char ifname[IF_NAMESIZE];
|
||||
|
||||
if (paddr->can_ifindex)
|
||||
str += sprintf(str, "%s", if_indextoname(paddr->can_ifindex, ifname));
|
||||
*str++ = ':';
|
||||
|
||||
if (paddr->can_addr.j1939.addr != J1939_NO_ADDR)
|
||||
str += sprintf(str, "%02x", paddr->can_addr.j1939.addr);
|
||||
*str++ = ',';
|
||||
if (paddr->can_addr.j1939.pgn != J1939_NO_PGN)
|
||||
str += sprintf(str, "%05x", paddr->can_addr.j1939.pgn);
|
||||
*str++ = ',';
|
||||
if (paddr->can_addr.j1939.name != J1939_NO_NAME)
|
||||
str += sprintf(str, "%016llx", paddr->can_addr.j1939.name);
|
||||
*str++ = 0;
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void onsigalrm(int sig)
|
||||
{
|
||||
error(0, 0, "exit as requested");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void schedule_oneshot_itimer(double delay)
|
||||
{
|
||||
struct itimerval it = {};
|
||||
|
||||
it.it_value.tv_sec = delay;
|
||||
it.it_value.tv_usec = (long)(delay * 1e6) % 1000000;
|
||||
if (setitimer(ITIMER_REAL, &it, NULL) < 0)
|
||||
error(1, errno, "schedule itimer %.3lfs", delay);
|
||||
}
|
||||
|
||||
/* main */
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int ret, sock, opt, j, verbose;
|
||||
socklen_t peernamelen;
|
||||
struct sockaddr_can sockname = {
|
||||
.can_family = AF_CAN,
|
||||
.can_addr.j1939 = {
|
||||
.addr = J1939_NO_ADDR,
|
||||
.name = J1939_NO_NAME,
|
||||
.pgn = J1939_NO_PGN,
|
||||
},
|
||||
}, peername = {
|
||||
.can_family = AF_CAN,
|
||||
.can_addr.j1939 = {
|
||||
.addr = J1939_NO_ADDR,
|
||||
.name = J1939_NO_NAME,
|
||||
.pgn = J1939_NO_PGN,
|
||||
},
|
||||
};
|
||||
uint8_t dat[128];
|
||||
int valid_peername = 0;
|
||||
int todo_send = 0, todo_recv = 0, todo_echo = 0, todo_prio = -1;
|
||||
int todo_connect = 0, todo_names = 0, todo_wait = 0;
|
||||
|
||||
/* argument parsing */
|
||||
while ((opt = getopt(argc, argv, optstring)) != -1)
|
||||
switch (opt) {
|
||||
case 'v':
|
||||
verbose = 1;
|
||||
break;
|
||||
case 's':
|
||||
todo_send = strtoul(optarg ?: "8", NULL, 0);
|
||||
break;
|
||||
case 'r':
|
||||
todo_recv = 1;
|
||||
break;
|
||||
case 'e':
|
||||
todo_echo = 1;
|
||||
break;
|
||||
case 'p':
|
||||
todo_prio = strtoul(optarg, NULL, 0);
|
||||
break;
|
||||
case 'c':
|
||||
todo_connect = 1;
|
||||
break;
|
||||
case 'n':
|
||||
todo_names = 1;
|
||||
break;
|
||||
case 'w':
|
||||
schedule_oneshot_itimer(strtod(optarg ?: "1", NULL));
|
||||
signal(SIGALRM, onsigalrm);
|
||||
todo_wait = 1;
|
||||
break;
|
||||
default:
|
||||
fputs(help_msg, stderr);
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
|
||||
if (argv[optind]) {
|
||||
if (strcmp("-", argv[optind]))
|
||||
parse_canaddr(argv[optind], &sockname);
|
||||
++optind;
|
||||
}
|
||||
|
||||
if (argv[optind]) {
|
||||
if (strcmp("-", argv[optind])) {
|
||||
parse_canaddr(argv[optind], &peername);
|
||||
valid_peername = 1;
|
||||
}
|
||||
++optind;
|
||||
}
|
||||
|
||||
/* open socket */
|
||||
if (verbose)
|
||||
fprintf(stderr, "- socket(PF_CAN, SOCK_DGRAM, CAN_J1939);\n");
|
||||
sock = ret = socket(PF_CAN, SOCK_DGRAM, CAN_J1939);
|
||||
if (ret < 0)
|
||||
error(1, errno, "socket(j1939)");
|
||||
|
||||
if (todo_prio >= 0) {
|
||||
if (verbose)
|
||||
fprintf(stderr, "- setsockopt(, SOL_CAN_J1939, SO_J1939_SEND_PRIO, &%i);\n", todo_prio);
|
||||
ret = setsockopt(sock, SOL_CAN_J1939, SO_J1939_SEND_PRIO,
|
||||
&todo_prio, sizeof(todo_prio));
|
||||
if (ret < 0)
|
||||
error(1, errno, "set priority %i", todo_prio);
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
fprintf(stderr, "- bind(, %s, %zi);\n", canaddr2str(&sockname), sizeof(sockname));
|
||||
ret = bind(sock, (void *)&sockname, sizeof(sockname));
|
||||
if (ret < 0)
|
||||
error(1, errno, "bind()");
|
||||
|
||||
if (todo_connect) {
|
||||
if (!valid_peername)
|
||||
error(1, 0, "no peername supplied");
|
||||
if (verbose)
|
||||
fprintf(stderr, "- connect(, %s, %zi);\n", canaddr2str(&peername), sizeof(peername));
|
||||
ret = connect(sock, (void *)&peername, sizeof(peername));
|
||||
if (ret < 0)
|
||||
error(1, errno, "connect()");
|
||||
}
|
||||
|
||||
if (todo_send) {
|
||||
/* initialize test vector */
|
||||
for (j = 0; j < sizeof(dat); ++j)
|
||||
dat[j] = ((2*j) << 4) + ((2*j+1) & 0xf);
|
||||
|
||||
/* send data */
|
||||
/*
|
||||
* when using connect, do not provide additional
|
||||
* destination information and use send()
|
||||
*/
|
||||
if (valid_peername && !todo_connect) {
|
||||
if (verbose)
|
||||
fprintf(stderr, "- sendto(, <dat>, %i, 0, %s, %zi);\n", todo_send, canaddr2str(&peername), sizeof(peername));
|
||||
ret = sendto(sock, dat, todo_send, 0,
|
||||
(void *)&peername, sizeof(peername));
|
||||
} else {
|
||||
/*
|
||||
* we may do sendto(sock, dat, todo_send, 0, NULL, 0)
|
||||
* as well, but using send() demonstrates the API better
|
||||
*/
|
||||
if (verbose)
|
||||
fprintf(stderr, "- send(, <dat>, %i, 0);\n", todo_send);
|
||||
ret = send(sock, dat, todo_send, 0);
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
error(1, errno, "sendto");
|
||||
}
|
||||
|
||||
/* main loop */
|
||||
if ((todo_echo || todo_recv) && verbose)
|
||||
fprintf(stderr, "- while (1)\n");
|
||||
while (todo_echo || todo_recv) {
|
||||
/*
|
||||
* re-use peername for storing the sender's peername of
|
||||
* received packets
|
||||
*/
|
||||
if (verbose)
|
||||
fprintf(stderr, "- recvfrom(, <dat>, %zi, 0, &<peername>, %zi);\n", sizeof(peername), sizeof(peername));
|
||||
peernamelen = sizeof(peername);
|
||||
ret = recvfrom(sock, dat, sizeof(dat), 0,
|
||||
(void *)&peername, &peernamelen);
|
||||
if (ret < 0) {
|
||||
if (EINTR == errno) {
|
||||
if (verbose)
|
||||
fprintf(stderr, "-\t<interrupted>\n");
|
||||
continue;
|
||||
}
|
||||
error(1, errno, "recvfrom()");
|
||||
}
|
||||
|
||||
if (todo_echo) {
|
||||
if (verbose)
|
||||
fprintf(stderr, "- sendto(, <dat>, %i, 0, %s, %i);\n", ret, canaddr2str(&peername), peernamelen);
|
||||
ret = sendto(sock, dat, ret, 0,
|
||||
(void *)&peername, peernamelen);
|
||||
if (ret < 0)
|
||||
error(1, errno, "sendto");
|
||||
}
|
||||
if (todo_recv) {
|
||||
if (todo_names && peername.can_addr.j1939.name)
|
||||
printf("%016llx ", peername.can_addr.j1939.name);
|
||||
printf("%02x %05x:", peername.can_addr.j1939.addr,
|
||||
peername.can_addr.j1939.pgn);
|
||||
for (j = 0; j < ret; ++j)
|
||||
printf(" %02x", dat[j]);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
if (todo_wait)
|
||||
for (;;)
|
||||
sleep(1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue