From 6e7f845a6886d69e14b6fb3796673dd1efd217e0 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Fri, 13 May 2022 20:00:45 +0200 Subject: [PATCH] isotpsend: add support for transmission without flow control Usually the ISO 15765-2 protocol is a point-to-point protocol to transfer segmented PDUs to a dedicated receiver. This receiver sends a flow control message to specify protocol options and timings (e.g. block size / STmin). The so called functional addressing communication allows a 1:N communication but is limited to a single frame length. The new CAN_ISOTP_CF_BROADCAST flag introduced in Linux 5.19 allows an unconfirmed 1:N communication with PDU lengths that would not fit into a single frame. This feature is not covered by the ISO 15765-2 standard. The isotpsend tool gets a new option '-C' to enable this unconfirmed 1:N communication by setting the CAN_ISOTP_CF_BROADCAST flag. Signed-off-by: Oliver Hartkopp --- include/linux/can/isotp.h | 25 +++++++++++++------------ isotpsend.c | 22 +++++++++++++++++----- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/include/linux/can/isotp.h b/include/linux/can/isotp.h index 590f8ae..439c982 100644 --- a/include/linux/can/isotp.h +++ b/include/linux/can/isotp.h @@ -124,18 +124,19 @@ struct can_isotp_ll_options { /* flags for isotp behaviour */ -#define CAN_ISOTP_LISTEN_MODE 0x001 /* listen only (do not send FC) */ -#define CAN_ISOTP_EXTEND_ADDR 0x002 /* enable extended addressing */ -#define CAN_ISOTP_TX_PADDING 0x004 /* enable CAN frame padding tx path */ -#define CAN_ISOTP_RX_PADDING 0x008 /* enable CAN frame padding rx path */ -#define CAN_ISOTP_CHK_PAD_LEN 0x010 /* check received CAN frame padding */ -#define CAN_ISOTP_CHK_PAD_DATA 0x020 /* check received CAN frame padding */ -#define CAN_ISOTP_HALF_DUPLEX 0x040 /* half duplex error state handling */ -#define CAN_ISOTP_FORCE_TXSTMIN 0x080 /* ignore stmin from received FC */ -#define CAN_ISOTP_FORCE_RXSTMIN 0x100 /* ignore CFs depending on rx stmin */ -#define CAN_ISOTP_RX_EXT_ADDR 0x200 /* different rx extended addressing */ -#define CAN_ISOTP_WAIT_TX_DONE 0x400 /* wait for tx completion */ -#define CAN_ISOTP_SF_BROADCAST 0x800 /* 1-to-N functional addressing */ +#define CAN_ISOTP_LISTEN_MODE 0x0001 /* listen only (do not send FC) */ +#define CAN_ISOTP_EXTEND_ADDR 0x0002 /* enable extended addressing */ +#define CAN_ISOTP_TX_PADDING 0x0004 /* enable CAN frame padding tx path */ +#define CAN_ISOTP_RX_PADDING 0x0008 /* enable CAN frame padding rx path */ +#define CAN_ISOTP_CHK_PAD_LEN 0x0010 /* check received CAN frame padding */ +#define CAN_ISOTP_CHK_PAD_DATA 0x0020 /* check received CAN frame padding */ +#define CAN_ISOTP_HALF_DUPLEX 0x0040 /* half duplex error state handling */ +#define CAN_ISOTP_FORCE_TXSTMIN 0x0080 /* ignore stmin from received FC */ +#define CAN_ISOTP_FORCE_RXSTMIN 0x0100 /* ignore CFs depending on rx stmin */ +#define CAN_ISOTP_RX_EXT_ADDR 0x0200 /* different rx extended addressing */ +#define CAN_ISOTP_WAIT_TX_DONE 0x0400 /* wait for tx completion */ +#define CAN_ISOTP_SF_BROADCAST 0x0800 /* 1-to-N functional addressing */ +#define CAN_ISOTP_CF_BROADCAST 0x1000 /* 1-to-N transmission w/o FC */ /* protocol machine default values */ diff --git a/isotpsend.c b/isotpsend.c index ef3d19d..deac601 100644 --- a/isotpsend.c +++ b/isotpsend.c @@ -75,7 +75,8 @@ void print_usage(char *prg) fprintf(stderr, " -l (send num PDUs - use 'i' for infinite loop)\n"); fprintf(stderr, " -g (wait given usecs before sending a PDU)\n"); fprintf(stderr, " -b (block until the PDU transmission is completed)\n"); - fprintf(stderr, " -S (SF broadcast mode for functional addressing)\n"); + fprintf(stderr, " -S (SF broadcast mode - for functional addressing)\n"); + fprintf(stderr, " -C (CF broadcast mode - no wait for flow controls)\n"); fprintf(stderr, " -L :: (link layer options for CAN FD)\n"); fprintf(stderr, "\nCAN IDs and addresses are given and expected in hexadecimal values.\n"); fprintf(stderr, "The pdu data is expected on STDIN in space separated ASCII hex values.\n"); @@ -101,7 +102,7 @@ int main(int argc, char **argv) addr.can_addr.tp.tx_id = addr.can_addr.tp.rx_id = NO_CAN_ID; - while ((opt = getopt(argc, argv, "s:d:x:p:P:t:f:D:l:g:bSL:?")) != -1) { + while ((opt = getopt(argc, argv, "s:d:x:p:P:t:f:D:l:g:bSCL:?")) != -1) { switch (opt) { case 's': addr.can_addr.tp.tx_id = strtoul(optarg, NULL, 16); @@ -211,6 +212,10 @@ int main(int argc, char **argv) opts.flags |= CAN_ISOTP_SF_BROADCAST; break; + case 'C': + opts.flags |= CAN_ISOTP_CF_BROADCAST; + break; + case 'L': if (sscanf(optarg, "%hhu:%hhu:%hhu", &llopts.mtu, @@ -235,20 +240,27 @@ int main(int argc, char **argv) } } +#define BC_FLAGS (CAN_ISOTP_SF_BROADCAST | CAN_ISOTP_CF_BROADCAST) + if ((argc - optind != 1) || (addr.can_addr.tp.tx_id == NO_CAN_ID) || + ((opts.flags & BC_FLAGS) == BC_FLAGS) || ((addr.can_addr.tp.rx_id == NO_CAN_ID) && - (!(opts.flags & CAN_ISOTP_SF_BROADCAST)))) { + (!(opts.flags & BC_FLAGS)))) { print_usage(basename(argv[0])); exit(1); } - + if ((s = socket(PF_CAN, SOCK_DGRAM, CAN_ISOTP)) < 0) { perror("socket"); exit(1); } - setsockopt(s, SOL_CAN_ISOTP, CAN_ISOTP_OPTS, &opts, sizeof(opts)); + if (setsockopt(s, SOL_CAN_ISOTP, CAN_ISOTP_OPTS, &opts, sizeof(opts)) < 0) { + perror("sockopt"); + exit(1); + } + if (llopts.tx_dl) { if (setsockopt(s, SOL_CAN_ISOTP, CAN_ISOTP_LL_OPTS, &llopts, sizeof(llopts)) < 0) {