Commit 9f39d365 authored by Oliver Hartkopp's avatar Oliver Hartkopp Committed by Marc Kleine-Budde

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.netSigned-off-by: default avatarOliver Hartkopp <socketcan@hartkopp.net>
Signed-off-by: default avatarMarc Kleine-Budde <mkl@pengutronix.de>
parent 51a0d5e5
...@@ -124,18 +124,19 @@ struct can_isotp_ll_options { ...@@ -124,18 +124,19 @@ struct can_isotp_ll_options {
/* flags for isotp behaviour */ /* flags for isotp behaviour */
#define CAN_ISOTP_LISTEN_MODE 0x001 /* listen only (do not send FC) */ #define CAN_ISOTP_LISTEN_MODE 0x0001 /* listen only (do not send FC) */
#define CAN_ISOTP_EXTEND_ADDR 0x002 /* enable extended addressing */ #define CAN_ISOTP_EXTEND_ADDR 0x0002 /* enable extended addressing */
#define CAN_ISOTP_TX_PADDING 0x004 /* enable CAN frame padding tx path */ #define CAN_ISOTP_TX_PADDING 0x0004 /* enable CAN frame padding tx path */
#define CAN_ISOTP_RX_PADDING 0x008 /* enable CAN frame padding rx path */ #define CAN_ISOTP_RX_PADDING 0x0008 /* enable CAN frame padding rx path */
#define CAN_ISOTP_CHK_PAD_LEN 0x010 /* check received CAN frame padding */ #define CAN_ISOTP_CHK_PAD_LEN 0x0010 /* check received CAN frame padding */
#define CAN_ISOTP_CHK_PAD_DATA 0x020 /* check received CAN frame padding */ #define CAN_ISOTP_CHK_PAD_DATA 0x0020 /* check received CAN frame padding */
#define CAN_ISOTP_HALF_DUPLEX 0x040 /* half duplex error state handling */ #define CAN_ISOTP_HALF_DUPLEX 0x0040 /* half duplex error state handling */
#define CAN_ISOTP_FORCE_TXSTMIN 0x080 /* ignore stmin from received FC */ #define CAN_ISOTP_FORCE_TXSTMIN 0x0080 /* ignore stmin from received FC */
#define CAN_ISOTP_FORCE_RXSTMIN 0x100 /* ignore CFs depending on rx stmin */ #define CAN_ISOTP_FORCE_RXSTMIN 0x0100 /* ignore CFs depending on rx stmin */
#define CAN_ISOTP_RX_EXT_ADDR 0x200 /* different rx extended addressing */ #define CAN_ISOTP_RX_EXT_ADDR 0x0200 /* different rx extended addressing */
#define CAN_ISOTP_WAIT_TX_DONE 0x400 /* wait for tx completion */ #define CAN_ISOTP_WAIT_TX_DONE 0x0400 /* wait for tx completion */
#define CAN_ISOTP_SF_BROADCAST 0x800 /* 1-to-N functional addressing */ #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 */ /* protocol machine default values */
......
...@@ -104,6 +104,7 @@ MODULE_ALIAS("can-proto-6"); ...@@ -104,6 +104,7 @@ MODULE_ALIAS("can-proto-6");
#define FC_CONTENT_SZ 3 /* flow control content size in byte (FS/BS/STmin) */ #define FC_CONTENT_SZ 3 /* flow control content size in byte (FS/BS/STmin) */
#define ISOTP_CHECK_PADDING (CAN_ISOTP_CHK_PAD_LEN | CAN_ISOTP_CHK_PAD_DATA) #define ISOTP_CHECK_PADDING (CAN_ISOTP_CHK_PAD_LEN | CAN_ISOTP_CHK_PAD_DATA)
#define ISOTP_ALL_BC_FLAGS (CAN_ISOTP_SF_BROADCAST | CAN_ISOTP_CF_BROADCAST)
/* Flow Status given in FC frame */ /* Flow Status given in FC frame */
#define ISOTP_FC_CTS 0 /* clear to send */ #define ISOTP_FC_CTS 0 /* clear to send */
...@@ -159,6 +160,23 @@ static inline struct isotp_sock *isotp_sk(const struct sock *sk) ...@@ -159,6 +160,23 @@ static inline struct isotp_sock *isotp_sk(const struct sock *sk)
return (struct isotp_sock *)sk; return (struct isotp_sock *)sk;
} }
static u32 isotp_bc_flags(struct isotp_sock *so)
{
return so->opt.flags & ISOTP_ALL_BC_FLAGS;
}
static bool isotp_register_rxid(struct isotp_sock *so)
{
/* no broadcast modes => register rx_id for FC frame reception */
return (isotp_bc_flags(so) == 0);
}
static bool isotp_register_txecho(struct isotp_sock *so)
{
/* all modes but SF_BROADCAST register for tx echo skbs */
return (isotp_bc_flags(so) != CAN_ISOTP_SF_BROADCAST);
}
static enum hrtimer_restart isotp_rx_timer_handler(struct hrtimer *hrtimer) static enum hrtimer_restart isotp_rx_timer_handler(struct hrtimer *hrtimer)
{ {
struct isotp_sock *so = container_of(hrtimer, struct isotp_sock, struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
...@@ -803,7 +821,6 @@ static void isotp_create_fframe(struct canfd_frame *cf, struct isotp_sock *so, ...@@ -803,7 +821,6 @@ static void isotp_create_fframe(struct canfd_frame *cf, struct isotp_sock *so,
cf->data[i] = so->tx.buf[so->tx.idx++]; cf->data[i] = so->tx.buf[so->tx.idx++];
so->tx.sn = 1; so->tx.sn = 1;
so->tx.state = ISOTP_WAIT_FIRST_FC;
} }
static void isotp_rcv_echo(struct sk_buff *skb, void *data) static void isotp_rcv_echo(struct sk_buff *skb, void *data)
...@@ -936,7 +953,7 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) ...@@ -936,7 +953,7 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
off = (so->tx.ll_dl > CAN_MAX_DLEN) ? 1 : 0; off = (so->tx.ll_dl > CAN_MAX_DLEN) ? 1 : 0;
/* does the given data fit into a single frame for SF_BROADCAST? */ /* does the given data fit into a single frame for SF_BROADCAST? */
if ((so->opt.flags & CAN_ISOTP_SF_BROADCAST) && if ((isotp_bc_flags(so) == CAN_ISOTP_SF_BROADCAST) &&
(size > so->tx.ll_dl - SF_PCI_SZ4 - ae - off)) { (size > so->tx.ll_dl - SF_PCI_SZ4 - ae - off)) {
err = -EINVAL; err = -EINVAL;
goto err_out_drop; goto err_out_drop;
...@@ -1000,12 +1017,41 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) ...@@ -1000,12 +1017,41 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
/* don't enable wait queue for a single frame transmission */ /* don't enable wait queue for a single frame transmission */
wait_tx_done = 0; wait_tx_done = 0;
} else { } else {
/* send first frame and wait for FC */ /* send first frame */
isotp_create_fframe(cf, so, ae); isotp_create_fframe(cf, so, ae);
/* start timeout for FC */ if (isotp_bc_flags(so) == CAN_ISOTP_CF_BROADCAST) {
hrtimer_sec = 1; /* set timer for FC-less operation (STmin = 0) */
if (so->opt.flags & CAN_ISOTP_FORCE_TXSTMIN)
so->tx_gap = ktime_set(0, so->force_tx_stmin);
else
so->tx_gap = ktime_set(0, so->frame_txtime);
/* disable wait for FCs due to activated block size */
so->txfc.bs = 0;
/* cfecho should have been zero'ed by init */
if (so->cfecho)
pr_notice_once("can-isotp: no fc cfecho %08X\n",
so->cfecho);
/* set consecutive frame echo tag */
so->cfecho = *(u32 *)cf->data;
/* switch directly to ISOTP_SENDING state */
so->tx.state = ISOTP_SENDING;
/* start timeout for unlikely lost echo skb */
hrtimer_sec = 2;
} else {
/* standard flow control check */
so->tx.state = ISOTP_WAIT_FIRST_FC;
/* start timeout for FC */
hrtimer_sec = 1;
}
hrtimer_start(&so->txtimer, ktime_set(hrtimer_sec, 0), hrtimer_start(&so->txtimer, ktime_set(hrtimer_sec, 0),
HRTIMER_MODE_REL_SOFT); HRTIMER_MODE_REL_SOFT);
} }
...@@ -1025,6 +1071,9 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) ...@@ -1025,6 +1071,9 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
if (hrtimer_sec) if (hrtimer_sec)
hrtimer_cancel(&so->txtimer); hrtimer_cancel(&so->txtimer);
/* reset consecutive frame echo tag */
so->cfecho = 0;
goto err_out_drop; goto err_out_drop;
} }
...@@ -1120,15 +1169,17 @@ static int isotp_release(struct socket *sock) ...@@ -1120,15 +1169,17 @@ static int isotp_release(struct socket *sock)
lock_sock(sk); lock_sock(sk);
/* remove current filters & unregister */ /* remove current filters & unregister */
if (so->bound && (!(so->opt.flags & CAN_ISOTP_SF_BROADCAST))) { if (so->bound && isotp_register_txecho(so)) {
if (so->ifindex) { if (so->ifindex) {
struct net_device *dev; struct net_device *dev;
dev = dev_get_by_index(net, so->ifindex); dev = dev_get_by_index(net, so->ifindex);
if (dev) { if (dev) {
can_rx_unregister(net, dev, so->rxid, if (isotp_register_rxid(so))
SINGLE_MASK(so->rxid), can_rx_unregister(net, dev, so->rxid,
isotp_rcv, sk); SINGLE_MASK(so->rxid),
isotp_rcv, sk);
can_rx_unregister(net, dev, so->txid, can_rx_unregister(net, dev, so->txid,
SINGLE_MASK(so->txid), SINGLE_MASK(so->txid),
isotp_rcv_echo, sk); isotp_rcv_echo, sk);
...@@ -1164,7 +1215,6 @@ static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len) ...@@ -1164,7 +1215,6 @@ static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len)
canid_t tx_id, rx_id; canid_t tx_id, rx_id;
int err = 0; int err = 0;
int notify_enetdown = 0; int notify_enetdown = 0;
int do_rx_reg = 1;
if (len < ISOTP_MIN_NAMELEN) if (len < ISOTP_MIN_NAMELEN)
return -EINVAL; return -EINVAL;
...@@ -1192,12 +1242,8 @@ static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len) ...@@ -1192,12 +1242,8 @@ static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len)
goto out; goto out;
} }
/* do not register frame reception for functional addressing */ /* ensure different CAN IDs when the rx_id is to be registered */
if (so->opt.flags & CAN_ISOTP_SF_BROADCAST) if (isotp_register_rxid(so) && rx_id == tx_id) {
do_rx_reg = 0;
/* do not validate rx address for functional addressing */
if (do_rx_reg && rx_id == tx_id) {
err = -EADDRNOTAVAIL; err = -EADDRNOTAVAIL;
goto out; goto out;
} }
...@@ -1222,10 +1268,11 @@ static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len) ...@@ -1222,10 +1268,11 @@ static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len)
ifindex = dev->ifindex; ifindex = dev->ifindex;
if (do_rx_reg) { if (isotp_register_rxid(so))
can_rx_register(net, dev, rx_id, SINGLE_MASK(rx_id), can_rx_register(net, dev, rx_id, SINGLE_MASK(rx_id),
isotp_rcv, sk, "isotp", sk); isotp_rcv, sk, "isotp", sk);
if (isotp_register_txecho(so)) {
/* no consecutive frame echo skb in flight */ /* no consecutive frame echo skb in flight */
so->cfecho = 0; so->cfecho = 0;
...@@ -1294,6 +1341,15 @@ static int isotp_setsockopt_locked(struct socket *sock, int level, int optname, ...@@ -1294,6 +1341,15 @@ static int isotp_setsockopt_locked(struct socket *sock, int level, int optname,
if (!(so->opt.flags & CAN_ISOTP_RX_EXT_ADDR)) if (!(so->opt.flags & CAN_ISOTP_RX_EXT_ADDR))
so->opt.rx_ext_address = so->opt.ext_address; so->opt.rx_ext_address = so->opt.ext_address;
/* these broadcast flags are not allowed together */
if (isotp_bc_flags(so) == ISOTP_ALL_BC_FLAGS) {
/* CAN_ISOTP_SF_BROADCAST is prioritized */
so->opt.flags &= ~CAN_ISOTP_CF_BROADCAST;
/* give user feedback on wrong config attempt */
ret = -EINVAL;
}
/* check for frame_txtime changes (0 => no changes) */ /* check for frame_txtime changes (0 => no changes) */
if (so->opt.frame_txtime) { if (so->opt.frame_txtime) {
if (so->opt.frame_txtime == CAN_ISOTP_FRAME_TXTIME_ZERO) if (so->opt.frame_txtime == CAN_ISOTP_FRAME_TXTIME_ZERO)
...@@ -1444,10 +1500,12 @@ static void isotp_notify(struct isotp_sock *so, unsigned long msg, ...@@ -1444,10 +1500,12 @@ static void isotp_notify(struct isotp_sock *so, unsigned long msg,
case NETDEV_UNREGISTER: case NETDEV_UNREGISTER:
lock_sock(sk); lock_sock(sk);
/* remove current filters & unregister */ /* remove current filters & unregister */
if (so->bound && (!(so->opt.flags & CAN_ISOTP_SF_BROADCAST))) { if (so->bound && isotp_register_txecho(so)) {
can_rx_unregister(dev_net(dev), dev, so->rxid, if (isotp_register_rxid(so))
SINGLE_MASK(so->rxid), can_rx_unregister(dev_net(dev), dev, so->rxid,
isotp_rcv, sk); SINGLE_MASK(so->rxid),
isotp_rcv, sk);
can_rx_unregister(dev_net(dev), dev, so->txid, can_rx_unregister(dev_net(dev), dev, so->txid,
SINGLE_MASK(so->txid), SINGLE_MASK(so->txid),
isotp_rcv_echo, sk); isotp_rcv_echo, sk);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment