#define ISOTP_FC_WT 1          /* wait */
 #define ISOTP_FC_OVFLW 2       /* overflow */
 
+#define ISOTP_FC_TIMEOUT 1     /* 1 sec */
+#define ISOTP_ECHO_TIMEOUT 2   /* 2 secs */
+
 enum {
        ISOTP_IDLE = 0,
        ISOTP_WAIT_FIRST_FC,
        so->lastrxcf_tstamp = ktime_set(0, 0);
 
        /* start rx timeout watchdog */
-       hrtimer_start(&so->rxtimer, ktime_set(1, 0), HRTIMER_MODE_REL_SOFT);
+       hrtimer_start(&so->rxtimer, ktime_set(ISOTP_FC_TIMEOUT, 0),
+                     HRTIMER_MODE_REL_SOFT);
        return 0;
 }
 
        return 0;
 }
 
+static void isotp_send_cframe(struct isotp_sock *so);
+
 static int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae)
 {
        struct sock *sk = &so->sk;
        case ISOTP_FC_CTS:
                so->tx.bs = 0;
                so->tx.state = ISOTP_SENDING;
-               /* start cyclic timer for sending CF frame */
-               hrtimer_start(&so->txtimer, so->tx_gap,
+               /* send CF frame and enable echo timeout handling */
+               hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
                              HRTIMER_MODE_REL_SOFT);
+               isotp_send_cframe(so);
                break;
 
        case ISOTP_FC_WT:
                /* start timer to wait for next FC frame */
-               hrtimer_start(&so->txtimer, ktime_set(1, 0),
+               hrtimer_start(&so->txtimer, ktime_set(ISOTP_FC_TIMEOUT, 0),
                              HRTIMER_MODE_REL_SOFT);
                break;
 
        /* perform blocksize handling, if enabled */
        if (!so->rxfc.bs || ++so->rx.bs < so->rxfc.bs) {
                /* start rx timeout watchdog */
-               hrtimer_start(&so->rxtimer, ktime_set(1, 0),
+               hrtimer_start(&so->rxtimer, ktime_set(ISOTP_FC_TIMEOUT, 0),
                              HRTIMER_MODE_REL_SOFT);
                return 0;
        }
        struct isotp_sock *so = isotp_sk(sk);
        struct canfd_frame *cf = (struct canfd_frame *)skb->data;
 
-       /* only handle my own local echo skb's */
+       /* only handle my own local echo CF/SF skb's (no FF!) */
        if (skb->sk != sk || so->cfecho != *(u32 *)cf->data)
                return;
 
        if (so->txfc.bs && so->tx.bs >= so->txfc.bs) {
                /* stop and wait for FC with timeout */
                so->tx.state = ISOTP_WAIT_FC;
-               hrtimer_start(&so->txtimer, ktime_set(1, 0),
+               hrtimer_start(&so->txtimer, ktime_set(ISOTP_FC_TIMEOUT, 0),
                              HRTIMER_MODE_REL_SOFT);
                return;
        }
 
        /* no gap between data frames needed => use burst mode */
        if (!so->tx_gap) {
+               /* enable echo timeout handling */
+               hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
+                             HRTIMER_MODE_REL_SOFT);
                isotp_send_cframe(so);
                return;
        }
                        /* start timeout for unlikely lost echo skb */
                        hrtimer_set_expires(&so->txtimer,
                                            ktime_add(ktime_get(),
-                                                     ktime_set(2, 0)));
+                                                     ktime_set(ISOTP_ECHO_TIMEOUT, 0)));
                        restart = HRTIMER_RESTART;
 
                        /* push out the next consecutive frame */
                break;
 
        default:
-               WARN_ON_ONCE(1);
+               WARN_ONCE(1, "can-isotp: tx timer state %08X cfecho %08X\n",
+                         so->tx.state, so->cfecho);
        }
 
        return restart;
        struct canfd_frame *cf;
        int ae = (so->opt.flags & CAN_ISOTP_EXTEND_ADDR) ? 1 : 0;
        int wait_tx_done = (so->opt.flags & CAN_ISOTP_WAIT_TX_DONE) ? 1 : 0;
-       s64 hrtimer_sec = 0;
+       s64 hrtimer_sec = ISOTP_ECHO_TIMEOUT;
        int off;
        int err;
 
                err = wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE);
                if (err)
                        goto err_out;
+
+               so->tx.state = ISOTP_SENDING;
        }
 
        if (!size || size > MAX_MSG_LENGTH) {
        cf = (struct canfd_frame *)skb->data;
        skb_put_zero(skb, so->ll.mtu);
 
+       /* cfecho should have been zero'ed by init / former isotp_rcv_echo() */
+       if (so->cfecho)
+               pr_notice_once("can-isotp: uninit cfecho %08X\n", so->cfecho);
+
        /* check for single frame transmission depending on TX_DL */
        if (size <= so->tx.ll_dl - SF_PCI_SZ4 - ae - off) {
                /* The message size generally fits into a SingleFrame - good.
                else
                        cf->data[ae] |= size;
 
-               so->tx.state = ISOTP_IDLE;
-               wake_up_interruptible(&so->wait);
-
-               /* don't enable wait queue for a single frame transmission */
-               wait_tx_done = 0;
+               /* set CF echo tag for isotp_rcv_echo() (SF-mode) */
+               so->cfecho = *(u32 *)cf->data;
        } else {
                /* send first frame */
 
                        /* 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 */
+                       /* set CF echo tag for isotp_rcv_echo() (CF-mode) */
                        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_sec = ISOTP_FC_TIMEOUT;
 
-               hrtimer_start(&so->txtimer, ktime_set(hrtimer_sec, 0),
-                             HRTIMER_MODE_REL_SOFT);
+                       /* no CF echo tag for isotp_rcv_echo() (FF-mode) */
+                       so->cfecho = 0;
+               }
        }
 
+       hrtimer_start(&so->txtimer, ktime_set(hrtimer_sec, 0),
+                     HRTIMER_MODE_REL_SOFT);
+
        /* send the first or only CAN frame */
        cf->flags = so->ll.tx_flags;
 
                               __func__, ERR_PTR(err));
 
                /* no transmission -> no timeout monitoring */
-               if (hrtimer_sec)
-                       hrtimer_cancel(&so->txtimer);
+               hrtimer_cancel(&so->txtimer);
 
                /* reset consecutive frame echo tag */
                so->cfecho = 0;