Merge branch 'master' of https://github.com/kurt-vd/test-can-j1939
commit
8839679a4c
|
|
@ -48,6 +48,7 @@ GNUmakefile.in
|
||||||
/slcan_attach
|
/slcan_attach
|
||||||
/slcand
|
/slcand
|
||||||
/slcanpty
|
/slcanpty
|
||||||
|
/testj1939
|
||||||
|
|
||||||
/can-utils-*.tar.bz2
|
/can-utils-*.tar.bz2
|
||||||
/can-utils-*.tar.gz
|
/can-utils-*.tar.gz
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,8 @@ bin_PROGRAMS = \
|
||||||
log2long \
|
log2long \
|
||||||
slcan_attach \
|
slcan_attach \
|
||||||
slcand \
|
slcand \
|
||||||
slcanpty
|
slcanpty \
|
||||||
|
testj1939
|
||||||
|
|
||||||
jacd_LDADD = libj1939.la
|
jacd_LDADD = libj1939.la
|
||||||
jspy_LDADD = libj1939.la
|
jspy_LDADD = libj1939.la
|
||||||
|
|
@ -73,7 +74,9 @@ EXTRA_DIST = \
|
||||||
.travis.yml \
|
.travis.yml \
|
||||||
Android.mk \
|
Android.mk \
|
||||||
README.md \
|
README.md \
|
||||||
autogen.sh
|
autogen.sh \
|
||||||
|
can-j1939-kickstart.md
|
||||||
|
can-j1939.md
|
||||||
|
|
||||||
MAINTAINERCLEANFILES = \
|
MAINTAINERCLEANFILES = \
|
||||||
configure \
|
configure \
|
||||||
|
|
|
||||||
2
Makefile
2
Makefile
|
|
@ -55,7 +55,7 @@ CPPFLAGS += -Iinclude \
|
||||||
PROGRAMS_ISOTP = isotpdump isotprecv isotpsend isotpsniffer isotptun isotpserver isotpperf
|
PROGRAMS_ISOTP = isotpdump isotprecv isotpsend isotpsniffer isotptun isotpserver isotpperf
|
||||||
PROGRAMS_CANGW = cangw
|
PROGRAMS_CANGW = cangw
|
||||||
PROGRAMS_SLCAN = slcan_attach slcand
|
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\
|
PROGRAMS = can-calc-bit-timing candump cansniffer cansend canplayer cangen canbusload\
|
||||||
log2long log2asc asc2log\
|
log2long log2asc asc2log\
|
||||||
canlogserver bcmserver\
|
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