Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

can: isotp: 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.

This new CAN_ISOTP_CF_BROADCAST allows an unconfirmed 1:N communication
with PDU length that would not fit into a single frame. This feature is
not covered by the ISO 15765-2 standard.

Link: https://lore.kernel.org/all/20220507115558.19065-1-socketcan@hartkopp.net
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>

authored by

Oliver Hartkopp and committed by
Marc Kleine-Budde
9f39d365 51a0d5e5

+92 -33
+13 -12
include/uapi/linux/can/isotp.h
··· 124 124 125 125 /* flags for isotp behaviour */ 126 126 127 - #define CAN_ISOTP_LISTEN_MODE 0x001 /* listen only (do not send FC) */ 128 - #define CAN_ISOTP_EXTEND_ADDR 0x002 /* enable extended addressing */ 129 - #define CAN_ISOTP_TX_PADDING 0x004 /* enable CAN frame padding tx path */ 130 - #define CAN_ISOTP_RX_PADDING 0x008 /* enable CAN frame padding rx path */ 131 - #define CAN_ISOTP_CHK_PAD_LEN 0x010 /* check received CAN frame padding */ 132 - #define CAN_ISOTP_CHK_PAD_DATA 0x020 /* check received CAN frame padding */ 133 - #define CAN_ISOTP_HALF_DUPLEX 0x040 /* half duplex error state handling */ 134 - #define CAN_ISOTP_FORCE_TXSTMIN 0x080 /* ignore stmin from received FC */ 135 - #define CAN_ISOTP_FORCE_RXSTMIN 0x100 /* ignore CFs depending on rx stmin */ 136 - #define CAN_ISOTP_RX_EXT_ADDR 0x200 /* different rx extended addressing */ 137 - #define CAN_ISOTP_WAIT_TX_DONE 0x400 /* wait for tx completion */ 138 - #define CAN_ISOTP_SF_BROADCAST 0x800 /* 1-to-N functional addressing */ 127 + #define CAN_ISOTP_LISTEN_MODE 0x0001 /* listen only (do not send FC) */ 128 + #define CAN_ISOTP_EXTEND_ADDR 0x0002 /* enable extended addressing */ 129 + #define CAN_ISOTP_TX_PADDING 0x0004 /* enable CAN frame padding tx path */ 130 + #define CAN_ISOTP_RX_PADDING 0x0008 /* enable CAN frame padding rx path */ 131 + #define CAN_ISOTP_CHK_PAD_LEN 0x0010 /* check received CAN frame padding */ 132 + #define CAN_ISOTP_CHK_PAD_DATA 0x0020 /* check received CAN frame padding */ 133 + #define CAN_ISOTP_HALF_DUPLEX 0x0040 /* half duplex error state handling */ 134 + #define CAN_ISOTP_FORCE_TXSTMIN 0x0080 /* ignore stmin from received FC */ 135 + #define CAN_ISOTP_FORCE_RXSTMIN 0x0100 /* ignore CFs depending on rx stmin */ 136 + #define CAN_ISOTP_RX_EXT_ADDR 0x0200 /* different rx extended addressing */ 137 + #define CAN_ISOTP_WAIT_TX_DONE 0x0400 /* wait for tx completion */ 138 + #define CAN_ISOTP_SF_BROADCAST 0x0800 /* 1-to-N functional addressing */ 139 + #define CAN_ISOTP_CF_BROADCAST 0x1000 /* 1-to-N transmission w/o FC */ 139 140 140 141 /* protocol machine default values */ 141 142
+79 -21
net/can/isotp.c
··· 104 104 #define FC_CONTENT_SZ 3 /* flow control content size in byte (FS/BS/STmin) */ 105 105 106 106 #define ISOTP_CHECK_PADDING (CAN_ISOTP_CHK_PAD_LEN | CAN_ISOTP_CHK_PAD_DATA) 107 + #define ISOTP_ALL_BC_FLAGS (CAN_ISOTP_SF_BROADCAST | CAN_ISOTP_CF_BROADCAST) 107 108 108 109 /* Flow Status given in FC frame */ 109 110 #define ISOTP_FC_CTS 0 /* clear to send */ ··· 158 157 static inline struct isotp_sock *isotp_sk(const struct sock *sk) 159 158 { 160 159 return (struct isotp_sock *)sk; 160 + } 161 + 162 + static u32 isotp_bc_flags(struct isotp_sock *so) 163 + { 164 + return so->opt.flags & ISOTP_ALL_BC_FLAGS; 165 + } 166 + 167 + static bool isotp_register_rxid(struct isotp_sock *so) 168 + { 169 + /* no broadcast modes => register rx_id for FC frame reception */ 170 + return (isotp_bc_flags(so) == 0); 171 + } 172 + 173 + static bool isotp_register_txecho(struct isotp_sock *so) 174 + { 175 + /* all modes but SF_BROADCAST register for tx echo skbs */ 176 + return (isotp_bc_flags(so) != CAN_ISOTP_SF_BROADCAST); 161 177 } 162 178 163 179 static enum hrtimer_restart isotp_rx_timer_handler(struct hrtimer *hrtimer) ··· 821 803 cf->data[i] = so->tx.buf[so->tx.idx++]; 822 804 823 805 so->tx.sn = 1; 824 - so->tx.state = ISOTP_WAIT_FIRST_FC; 825 806 } 826 807 827 808 static void isotp_rcv_echo(struct sk_buff *skb, void *data) ··· 953 936 off = (so->tx.ll_dl > CAN_MAX_DLEN) ? 1 : 0; 954 937 955 938 /* does the given data fit into a single frame for SF_BROADCAST? */ 956 - if ((so->opt.flags & CAN_ISOTP_SF_BROADCAST) && 939 + if ((isotp_bc_flags(so) == CAN_ISOTP_SF_BROADCAST) && 957 940 (size > so->tx.ll_dl - SF_PCI_SZ4 - ae - off)) { 958 941 err = -EINVAL; 959 942 goto err_out_drop; ··· 1017 1000 /* don't enable wait queue for a single frame transmission */ 1018 1001 wait_tx_done = 0; 1019 1002 } else { 1020 - /* send first frame and wait for FC */ 1003 + /* send first frame */ 1021 1004 1022 1005 isotp_create_fframe(cf, so, ae); 1023 1006 1024 - /* start timeout for FC */ 1025 - hrtimer_sec = 1; 1007 + if (isotp_bc_flags(so) == CAN_ISOTP_CF_BROADCAST) { 1008 + /* set timer for FC-less operation (STmin = 0) */ 1009 + if (so->opt.flags & CAN_ISOTP_FORCE_TXSTMIN) 1010 + so->tx_gap = ktime_set(0, so->force_tx_stmin); 1011 + else 1012 + so->tx_gap = ktime_set(0, so->frame_txtime); 1013 + 1014 + /* disable wait for FCs due to activated block size */ 1015 + so->txfc.bs = 0; 1016 + 1017 + /* cfecho should have been zero'ed by init */ 1018 + if (so->cfecho) 1019 + pr_notice_once("can-isotp: no fc cfecho %08X\n", 1020 + so->cfecho); 1021 + 1022 + /* set consecutive frame echo tag */ 1023 + so->cfecho = *(u32 *)cf->data; 1024 + 1025 + /* switch directly to ISOTP_SENDING state */ 1026 + so->tx.state = ISOTP_SENDING; 1027 + 1028 + /* start timeout for unlikely lost echo skb */ 1029 + hrtimer_sec = 2; 1030 + } else { 1031 + /* standard flow control check */ 1032 + so->tx.state = ISOTP_WAIT_FIRST_FC; 1033 + 1034 + /* start timeout for FC */ 1035 + hrtimer_sec = 1; 1036 + } 1037 + 1026 1038 hrtimer_start(&so->txtimer, ktime_set(hrtimer_sec, 0), 1027 1039 HRTIMER_MODE_REL_SOFT); 1028 1040 } ··· 1070 1024 /* no transmission -> no timeout monitoring */ 1071 1025 if (hrtimer_sec) 1072 1026 hrtimer_cancel(&so->txtimer); 1027 + 1028 + /* reset consecutive frame echo tag */ 1029 + so->cfecho = 0; 1073 1030 1074 1031 goto err_out_drop; 1075 1032 } ··· 1169 1120 lock_sock(sk); 1170 1121 1171 1122 /* remove current filters & unregister */ 1172 - if (so->bound && (!(so->opt.flags & CAN_ISOTP_SF_BROADCAST))) { 1123 + if (so->bound && isotp_register_txecho(so)) { 1173 1124 if (so->ifindex) { 1174 1125 struct net_device *dev; 1175 1126 1176 1127 dev = dev_get_by_index(net, so->ifindex); 1177 1128 if (dev) { 1178 - can_rx_unregister(net, dev, so->rxid, 1179 - SINGLE_MASK(so->rxid), 1180 - isotp_rcv, sk); 1129 + if (isotp_register_rxid(so)) 1130 + can_rx_unregister(net, dev, so->rxid, 1131 + SINGLE_MASK(so->rxid), 1132 + isotp_rcv, sk); 1133 + 1181 1134 can_rx_unregister(net, dev, so->txid, 1182 1135 SINGLE_MASK(so->txid), 1183 1136 isotp_rcv_echo, sk); ··· 1215 1164 canid_t tx_id, rx_id; 1216 1165 int err = 0; 1217 1166 int notify_enetdown = 0; 1218 - int do_rx_reg = 1; 1219 1167 1220 1168 if (len < ISOTP_MIN_NAMELEN) 1221 1169 return -EINVAL; ··· 1242 1192 goto out; 1243 1193 } 1244 1194 1245 - /* do not register frame reception for functional addressing */ 1246 - if (so->opt.flags & CAN_ISOTP_SF_BROADCAST) 1247 - do_rx_reg = 0; 1248 - 1249 - /* do not validate rx address for functional addressing */ 1250 - if (do_rx_reg && rx_id == tx_id) { 1195 + /* ensure different CAN IDs when the rx_id is to be registered */ 1196 + if (isotp_register_rxid(so) && rx_id == tx_id) { 1251 1197 err = -EADDRNOTAVAIL; 1252 1198 goto out; 1253 1199 } ··· 1268 1222 1269 1223 ifindex = dev->ifindex; 1270 1224 1271 - if (do_rx_reg) { 1225 + if (isotp_register_rxid(so)) 1272 1226 can_rx_register(net, dev, rx_id, SINGLE_MASK(rx_id), 1273 1227 isotp_rcv, sk, "isotp", sk); 1274 1228 1229 + if (isotp_register_txecho(so)) { 1275 1230 /* no consecutive frame echo skb in flight */ 1276 1231 so->cfecho = 0; 1277 1232 ··· 1340 1293 /* no separate rx_ext_address is given => use ext_address */ 1341 1294 if (!(so->opt.flags & CAN_ISOTP_RX_EXT_ADDR)) 1342 1295 so->opt.rx_ext_address = so->opt.ext_address; 1296 + 1297 + /* these broadcast flags are not allowed together */ 1298 + if (isotp_bc_flags(so) == ISOTP_ALL_BC_FLAGS) { 1299 + /* CAN_ISOTP_SF_BROADCAST is prioritized */ 1300 + so->opt.flags &= ~CAN_ISOTP_CF_BROADCAST; 1301 + 1302 + /* give user feedback on wrong config attempt */ 1303 + ret = -EINVAL; 1304 + } 1343 1305 1344 1306 /* check for frame_txtime changes (0 => no changes) */ 1345 1307 if (so->opt.frame_txtime) { ··· 1500 1444 case NETDEV_UNREGISTER: 1501 1445 lock_sock(sk); 1502 1446 /* remove current filters & unregister */ 1503 - if (so->bound && (!(so->opt.flags & CAN_ISOTP_SF_BROADCAST))) { 1504 - can_rx_unregister(dev_net(dev), dev, so->rxid, 1505 - SINGLE_MASK(so->rxid), 1506 - isotp_rcv, sk); 1447 + if (so->bound && isotp_register_txecho(so)) { 1448 + if (isotp_register_rxid(so)) 1449 + can_rx_unregister(dev_net(dev), dev, so->rxid, 1450 + SINGLE_MASK(so->rxid), 1451 + isotp_rcv, sk); 1452 + 1507 1453 can_rx_unregister(dev_net(dev), dev, so->txid, 1508 1454 SINGLE_MASK(so->txid), 1509 1455 isotp_rcv_echo, sk);