From 198c5801ecf3dd62398923119239eccc04c026d9 Mon Sep 17 00:00:00 2001 From: Kurt Van Dijck Date: Mon, 18 Nov 2013 23:30:46 +0100 Subject: [PATCH 2/9] import sample program & help page --- Makefile | 11 ++ j1939.page | 327 ++++++++++++++++++++++++++++++++++++++++++++++++++++ page.theme | 21 ++++ style.css | 68 +++++++++++ testj1939.c | 200 ++++++++++++++++++++++++++++++++ 5 files changed, 627 insertions(+) create mode 100644 Makefile create mode 100644 j1939.page create mode 100644 page.theme create mode 100644 style.css create mode 100644 testj1939.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ee9b43c --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ + +OUTPUT = j1939.html testj1939 +default: $(OUTPUT) + +%.html: %.page page.theme + theme -f -o $@ $< -p "$*" + +CFLAGS = -Wall -g3 -O0 + +clean: + rm -f $(wildcard *.o) $(OUTPUT) diff --git a/j1939.page b/j1939.page new file mode 100644 index 0000000..1a8f2a5 --- /dev/null +++ b/j1939.page @@ -0,0 +1,327 @@ +# CAN-J1939 on linux + +## 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 + +CAN has no protocol id field. +Enabling protocols must be done manually + +### netlink + + ip link set can0 j1939 on + +### 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 + +### Static addressing + + ip addr add j1939 0x80 dev can0 + +### Dynamic addressing + + ip addr add j1939 name 0x012345678abcdef dev can0 + +# First steps with j1939 + +Use [testj1939](testj1939.c) + +Load modules, when vcan & can-j1939 are not in-kernel + + modprobe vcan + modprobe can-j1939 + +### create virtual CAN bus + +Make sure *can0* is available, or replace *can0* with *vcan0* + + ip link add can0 type vcan + +### enable CAN + + ip link set can0 up + +### enable j1939 + + modprobe can-j1939 + +### 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. + +### Use source address + + ./testj1939 can0:0x80 + +will say + + ./testj1939: bind(): Cannot assign requested address + +Since J1939 maintains addressing, **0x80** has not yet been assigned +as an address on **can0** . This behaviour is very similar to IP +addressing: you cannot bind to an address that is not your own. + +Now tell the kernel that we *own* address 0x80. +It will be available from now on. + + ip addr add j1939 0x80 dev can0 + ./testj1939 can0:0x80 + +now succeeds. + +### 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 + +This produces **1BFFFF80#0123456789ABCDEF** on CAN. + +### Multiple source addresses on 1 CAN device + + ip addr add j1939 0x90 dev can0 + + ./testj1939 -s can0:0x90 + +produces **1BFFFF90#0123456789ABCDEF** , + + ./testj1939 -s can0: + +still produces **1BFFFF80#0123456789ABCDEF** , since **0x80** +is the default _source address_. +Check + + ip addr show can0 + +emits + + X: can0: mtu 16 qdisc noqueue state UNKNOWN + link/can + can-j1939 0x80 scope link + can-j1939 0x90 scope link + +0x80 is the first address on can0. + +### Use specific PGN + + ./testj1939 -s can0:,0x12345 + +emits **1923FF80#0123456789ABCDEF** . + +Note that the real PGN is **0x12300**, and destination address is **0xff**. + +### Emit destination specific packets + +The destination field may be set during sendto(). +*testj1939* implements that like this + + ./testj1939 -s can0:,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. + + ./testj1939 -s can0:,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 + 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. + + ./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 + +## dynamic addressing + diff --git a/page.theme b/page.theme new file mode 100644 index 0000000..e850a9b --- /dev/null +++ b/page.theme @@ -0,0 +1,21 @@ + + + + page: <?theme title?> + + + + +
+ +
+ + + + + diff --git a/style.css b/style.css new file mode 100644 index 0000000..6be67d2 --- /dev/null +++ b/style.css @@ -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; +} diff --git a/testj1939.c b/testj1939.c new file mode 100644 index 0000000..fb3cba1 --- /dev/null +++ b/testj1939.c @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2013 EIA Electronics + * + * Authors: + * Kurt Van Dijck + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +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" + " -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" + "\n" + "Example:\n" + "testj1939 can1 20\n" + "\n" + ; + +static const char optstring[] = "?vs::rep:cn"; + +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); +} + +/* main */ +int main(int argc, char *argv[]) +{ + int ret, sock, opt, j; + 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 todo_send = 0, todo_recv = 0, todo_echo = 0, todo_prio = -1; + int todo_connect = 0, todo_names = 0; + + /* argument parsing */ + while ((opt = getopt(argc, argv, optstring)) != -1) + switch (opt) { + 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; + 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); + ++optind; + } + + /* open socket */ + sock = ret = socket(PF_CAN, SOCK_DGRAM, CAN_J1939); + if (ret < 0) + error(1, errno, "socket(j1939)"); + + if (todo_prio >= 0) { + 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); + } + + ret = bind(sock, (void *)&sockname, sizeof(sockname)); + if (ret < 0) + error(1, errno, "bind()"); + + if (todo_connect) { + 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 */ + ret = sendto(sock, dat, todo_send, 0, + (void *)&peername, sizeof(peername)); + if (ret < 0) + error(1, errno, "sendto"); + } + + /* main loop */ + while (todo_echo || todo_recv) { + /* + * re-use peername for storing the sender's peername of + * received packets + */ + peernamelen = sizeof(peername); + ret = recvfrom(sock, dat, sizeof(dat), 0, + (void *)&peername, &peernamelen); + if (ret < 0) { + if (EINTR == errno) + continue; + error(1, errno, "recvfrom()"); + } + + if (todo_echo) { + 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"); + } + } + return 0; +} + From 77c08edead0227b5246003c8d0fbbcf75336d80c Mon Sep 17 00:00:00 2001 From: Kurt Van Dijck Date: Wed, 20 Nov 2013 11:20:24 +0100 Subject: [PATCH 3/9] add .gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eeaa484 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +# output +testj1939 +j1939.html From d66e6ce4eea1db529a765e73243b842edc8d1756 Mon Sep 17 00:00:00 2001 From: Kurt Van Dijck Date: Wed, 20 Nov 2013 11:17:27 +0100 Subject: [PATCH 4/9] testj1939: remember if peername was provided When peername was not provided, the program uses send() rather than sendto() so the API calls are better demonstrated --- testj1939.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/testj1939.c b/testj1939.c index fb3cba1..aa9ca20 100644 --- a/testj1939.c +++ b/testj1939.c @@ -88,6 +88,7 @@ int main(int argc, char *argv[]) }, }; 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; @@ -125,8 +126,10 @@ int main(int argc, char *argv[]) } if (argv[optind]) { - if (strcmp("-", argv[optind])) + if (strcmp("-", argv[optind])) { parse_canaddr(argv[optind], &peername); + valid_peername = 1; + } ++optind; } @@ -147,6 +150,8 @@ int main(int argc, char *argv[]) error(1, errno, "bind()"); if (todo_connect) { + if (!valid_peername) + error(1, 0, "no peername supplied"); ret = connect(sock, (void *)&peername, sizeof(peername)); if (ret < 0) error(1, errno, "connect()"); @@ -158,8 +163,16 @@ int main(int argc, char *argv[]) dat[j] = ((2*j) << 4) + ((2*j+1) & 0xf); /* send data */ - ret = sendto(sock, dat, todo_send, 0, - (void *)&peername, sizeof(peername)); + if (valid_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 + */ + ret = send(sock, dat, todo_send, 0); + if (ret < 0) error(1, errno, "sendto"); } From 854f47fa84879ef4778e444ec61c7b11f8c878f7 Mon Sep 17 00:00:00 2001 From: Kurt Van Dijck Date: Wed, 20 Nov 2013 11:19:40 +0100 Subject: [PATCH 5/9] testj1939: use send() when connect()ed When using connect(), don't re-use the peername but use send() without destination information --- testj1939.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/testj1939.c b/testj1939.c index aa9ca20..f3b61df 100644 --- a/testj1939.c +++ b/testj1939.c @@ -163,7 +163,11 @@ int main(int argc, char *argv[]) dat[j] = ((2*j) << 4) + ((2*j+1) & 0xf); /* send data */ - if (valid_peername) + /* + * when using connect, do not provide additional + * destination information and use send() + */ + if (valid_peername && !todo_connect) ret = sendto(sock, dat, todo_send, 0, (void *)&peername, sizeof(peername)); else From 7fb5f009fd5b172f264666fc6a565264bfd5d629 Mon Sep 17 00:00:00 2001 From: Kurt Van Dijck Date: Thu, 28 Nov 2013 09:56:37 +0100 Subject: [PATCH 6/9] j1939.page: restructure --- j1939.page | 63 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/j1939.page b/j1939.page index 1a8f2a5..9ab6ba4 100644 --- a/j1939.page +++ b/j1939.page @@ -120,29 +120,38 @@ This API is dropped for kernels with netlink support! ip addr add j1939 name 0x012345678abcdef dev can0 -# First steps with j1939 +# Kickstart guide to 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) -Load modules, when vcan & can-j1939 are not in-kernel - - modprobe vcan - modprobe can-j1939 - -### create virtual CAN bus - -Make sure *can0* is available, or replace *can0* with *vcan0* - - ip link add can0 type vcan - -### enable CAN - - ip link set can0 up - -### enable j1939 +When *can-j1939* is compiled as module, load it. modprobe can-j1939 +Enable the j1939 protocol stack on the CAN device + + ip link set can0 j1939 on + +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. + ### receive without source address Do in terminal 1 @@ -166,7 +175,7 @@ now emit this CAN message: 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. +is not meant for us and *testj1939* does not receive it. ### Use source address @@ -216,6 +225,11 @@ And to these test in another terminal This produces **1BFFFF80#0123456789ABCDEF** on CAN. + ./testj1939 -s can0: + +will produce exactly the same because **0x80** is the only +address currently assigned to **can0:** and is used by default. + ### Multiple source addresses on 1 CAN device ip addr add j1939 0x90 dev can0 @@ -271,9 +285,18 @@ 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:,0x12300 :,0x32100 + +emits **1B21FF80#0123456789ABCDEF** rather than 1923FF80#012345678ABCDEF + +Desitination specific transmissions + ./testj1939 -s can0:,0x12300 :0x40,0x32100 emits **1B214080#0123456789ABCDEF** . + It makes sometimes sense to omit the PGN in __bind( *sockname* )__ . ### Larger packets @@ -309,7 +332,7 @@ emits: The flow control causes a bit overhead. This overhead scales very good for larger J1939 packets. -# Advanced topics with j1939 +## Advanced topics with j1939 ### Change priority of J1939 packets @@ -323,5 +346,7 @@ emits ### using connect +### advanced filtering + ## dynamic addressing From 234729507ee07174237908cd5d78cc4d1c740369 Mon Sep 17 00:00:00 2001 From: Kurt Van Dijck Date: Thu, 28 Nov 2013 09:56:54 +0100 Subject: [PATCH 7/9] j1939.page: add relevant API calls --- j1939.page | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/j1939.page b/j1939.page index 9ab6ba4..f9c593e 100644 --- a/j1939.page +++ b/j1939.page @@ -152,6 +152,10 @@ 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. +Where I think it's relevant, I added the core API calls that *testj1939* used +because *testj1939* may look too generic to appreciate the degree of control +that the API exposes. + ### receive without source address Do in terminal 1 @@ -177,6 +181,14 @@ 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. +*testj1939* used these calls: + + sock = ret = socket(PF_CAN, SOCK_DGRAM, CAN_J1939); + ret = bind(sock, (void *)&sockname, sizeof(sockname)); + while (1) + ret = recvfrom(sock, dat, sizeof(dat), 0, + (void *)&peername, &peernamelen); + ### Use source address ./testj1939 can0:0x80 @@ -230,6 +242,12 @@ This produces **1BFFFF80#0123456789ABCDEF** on CAN. will produce exactly the same because **0x80** is the only address currently assigned to **can0:** and is used by default. +*testj1939* used these calls: + + sock = ret = socket(PF_CAN, SOCK_DGRAM, CAN_J1939); + ret = bind(sock, (void *)&sockname, sizeof(sockname)); + ret = send(sock, dat, todo_send, 0); + ### Multiple source addresses on 1 CAN device ip addr add j1939 0x90 dev can0 @@ -279,6 +297,13 @@ Specifing one during bind is therefore sufficient. emits the very same. +*testj1939* used these calls: + + sock = ret = socket(PF_CAN, SOCK_DGRAM, CAN_J1939); + ret = bind(sock, (void *)&sockname, sizeof(sockname)); + ret = sendto(sock, dat, todo_send, 0, + (void *)&peername, sizeof(peername)); + ### Emit different PGNs using the same socket The PGN is provided in both __bind( *sockname* )__ and @@ -299,6 +324,13 @@ emits **1B214080#0123456789ABCDEF** . It makes sometimes sense to omit the PGN in __bind( *sockname* )__ . +*testj1939* used these calls, even for the broadcasted transmission: + + sock = ret = socket(PF_CAN, SOCK_DGRAM, CAN_J1939); + ret = bind(sock, (void *)&sockname, sizeof(sockname)); + ret = sendto(sock, dat, todo_send, 0, + (void *)&peername, sizeof(peername)); + ### Larger packets J1939 transparently switches to *Transport Protocol* when packets @@ -313,6 +345,13 @@ emits: 18EBFF80#02EF0123456789AB 18EBFF80#03CDEF01234567 +*testj1939* used these same calls: + + sock = ret = socket(PF_CAN, SOCK_DGRAM, CAN_J1939); + ret = bind(sock, (void *)&sockname, sizeof(sockname)); + ret = sendto(sock, dat, todo_send, 0, + (void *)&peername, sizeof(peername)); + The fragments for broadcasted *Transport Protocol* are seperated __50ms__ from each other. Destination specific *Transport Protocol* applies flow control @@ -344,6 +383,14 @@ emits 1801FF80#0123456789ABCDEF 0C02FF80#0123456789ABCDEF +*testj1939* used these calls for modified priority: + + sock = ret = socket(PF_CAN, SOCK_DGRAM, CAN_J1939); + ret = setsockopt(sock, SOL_CAN_J1939, SO_J1939_SEND_PRIO, + &todo_prio, sizeof(todo_prio)); + ret = bind(sock, (void *)&sockname, sizeof(sockname)); + ret = send(sock, dat, todo_send, 0); + ### using connect ### advanced filtering From 75dfd666c4eb355532ee753ccf89f5c7d62579e7 Mon Sep 17 00:00:00 2001 From: Kurt Van Dijck Date: Thu, 28 Nov 2013 10:12:00 +0100 Subject: [PATCH 8/9] testj1939: print API calls --- testj1939.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/testj1939.c b/testj1939.c index f3b61df..8524f5b 100644 --- a/testj1939.c +++ b/testj1939.c @@ -31,6 +31,7 @@ static const char help_msg[] = "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" @@ -67,10 +68,32 @@ static void parse_canaddr(char *spec, struct sockaddr_can *paddr) 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; +} + /* main */ int main(int argc, char *argv[]) { - int ret, sock, opt, j; + int ret, sock, opt, j, verbose; socklen_t peernamelen; struct sockaddr_can sockname = { .can_family = AF_CAN, @@ -95,6 +118,9 @@ int main(int argc, char *argv[]) /* 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; @@ -134,17 +160,23 @@ int main(int argc, char *argv[]) } /* 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, %li);\n", canaddr2str(&sockname), sizeof(sockname)); ret = bind(sock, (void *)&sockname, sizeof(sockname)); if (ret < 0) error(1, errno, "bind()"); @@ -152,6 +184,8 @@ int main(int argc, char *argv[]) if (todo_connect) { if (!valid_peername) error(1, 0, "no peername supplied"); + if (verbose) + fprintf(stderr, "- connect(, %s, %li);\n", canaddr2str(&peername), sizeof(peername)); ret = connect(sock, (void *)&peername, sizeof(peername)); if (ret < 0) error(1, errno, "connect()"); @@ -167,36 +201,50 @@ int main(int argc, char *argv[]) * when using connect, do not provide additional * destination information and use send() */ - if (valid_peername && !todo_connect) + if (valid_peername && !todo_connect) { + if (verbose) + fprintf(stderr, "- sendto(, , %i, 0, %s, %li);\n", todo_send, canaddr2str(&peername), sizeof(peername)); ret = sendto(sock, dat, todo_send, 0, (void *)&peername, sizeof(peername)); - else + } 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(, , %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(, , %li, 0, &, %li);\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 (EINTR == errno) { + if (verbose) + fprintf(stderr, "-\t\n"); continue; + } error(1, errno, "recvfrom()"); } if (todo_echo) { + if (verbose) + fprintf(stderr, "- sendto(, , %i, 0, %s, %i);\n", ret, canaddr2str(&peername), peernamelen); ret = sendto(sock, dat, ret, 0, (void *)&peername, peernamelen); if (ret < 0) From 8e199945be50c349f3fcd64f707f726c5d2eddad Mon Sep 17 00:00:00 2001 From: Kurt Van Dijck Date: Thu, 28 Nov 2013 11:00:19 +0100 Subject: [PATCH 9/9] Revert "j1939.page: add relevant API calls" This reverts commit 234729507ee07174237908cd5d78cc4d1c740369. --- j1939.page | 47 +---------------------------------------------- 1 file changed, 1 insertion(+), 46 deletions(-) diff --git a/j1939.page b/j1939.page index f9c593e..b9592e8 100644 --- a/j1939.page +++ b/j1939.page @@ -152,9 +152,7 @@ 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. -Where I think it's relevant, I added the core API calls that *testj1939* used -because *testj1939* may look too generic to appreciate the degree of control -that the API exposes. +testj1939 can be told to print the used API calls by adding **-v** program argument. ### receive without source address @@ -181,14 +179,6 @@ 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. -*testj1939* used these calls: - - sock = ret = socket(PF_CAN, SOCK_DGRAM, CAN_J1939); - ret = bind(sock, (void *)&sockname, sizeof(sockname)); - while (1) - ret = recvfrom(sock, dat, sizeof(dat), 0, - (void *)&peername, &peernamelen); - ### Use source address ./testj1939 can0:0x80 @@ -242,12 +232,6 @@ This produces **1BFFFF80#0123456789ABCDEF** on CAN. will produce exactly the same because **0x80** is the only address currently assigned to **can0:** and is used by default. -*testj1939* used these calls: - - sock = ret = socket(PF_CAN, SOCK_DGRAM, CAN_J1939); - ret = bind(sock, (void *)&sockname, sizeof(sockname)); - ret = send(sock, dat, todo_send, 0); - ### Multiple source addresses on 1 CAN device ip addr add j1939 0x90 dev can0 @@ -297,13 +281,6 @@ Specifing one during bind is therefore sufficient. emits the very same. -*testj1939* used these calls: - - sock = ret = socket(PF_CAN, SOCK_DGRAM, CAN_J1939); - ret = bind(sock, (void *)&sockname, sizeof(sockname)); - ret = sendto(sock, dat, todo_send, 0, - (void *)&peername, sizeof(peername)); - ### Emit different PGNs using the same socket The PGN is provided in both __bind( *sockname* )__ and @@ -324,13 +301,6 @@ emits **1B214080#0123456789ABCDEF** . It makes sometimes sense to omit the PGN in __bind( *sockname* )__ . -*testj1939* used these calls, even for the broadcasted transmission: - - sock = ret = socket(PF_CAN, SOCK_DGRAM, CAN_J1939); - ret = bind(sock, (void *)&sockname, sizeof(sockname)); - ret = sendto(sock, dat, todo_send, 0, - (void *)&peername, sizeof(peername)); - ### Larger packets J1939 transparently switches to *Transport Protocol* when packets @@ -345,13 +315,6 @@ emits: 18EBFF80#02EF0123456789AB 18EBFF80#03CDEF01234567 -*testj1939* used these same calls: - - sock = ret = socket(PF_CAN, SOCK_DGRAM, CAN_J1939); - ret = bind(sock, (void *)&sockname, sizeof(sockname)); - ret = sendto(sock, dat, todo_send, 0, - (void *)&peername, sizeof(peername)); - The fragments for broadcasted *Transport Protocol* are seperated __50ms__ from each other. Destination specific *Transport Protocol* applies flow control @@ -383,14 +346,6 @@ emits 1801FF80#0123456789ABCDEF 0C02FF80#0123456789ABCDEF -*testj1939* used these calls for modified priority: - - sock = ret = socket(PF_CAN, SOCK_DGRAM, CAN_J1939); - ret = setsockopt(sock, SOL_CAN_J1939, SO_J1939_SEND_PRIO, - &todo_prio, sizeof(todo_prio)); - ret = bind(sock, (void *)&sockname, sizeof(sockname)); - ret = send(sock, dat, todo_send, 0); - ### using connect ### advanced filtering