enum j1939_xtp_abort {
J1939_XTP_NO_ABORT = 0,
J1939_XTP_ABORT_BUSY = 1, /* Already in one or more connection managed sessions and * cannot support another. * * EALREADY: * Operation already in progress
*/
J1939_XTP_ABORT_RESOURCE = 2, /* System resources were needed for another task so this * connection managed session was terminated. * * EMSGSIZE: * The socket type requires that message be sent atomically, * and the size of the message to be sent made this * impossible.
*/
J1939_XTP_ABORT_TIMEOUT = 3, /* A timeout occurred and this is the connection abort to * close the session. * * EHOSTUNREACH: * The destination host cannot be reached (probably because * the host is down or a remote router cannot reach it).
*/
J1939_XTP_ABORT_GENERIC = 4, /* CTS messages received when data transfer is in progress * * EBADMSG: * Not a data message
*/
J1939_XTP_ABORT_FAULT = 5, /* Maximal retransmit request limit reached * * ENOTRECOVERABLE: * State not recoverable
*/
J1939_XTP_ABORT_UNEXPECTED_DATA = 6, /* Unexpected data transfer packet * * ENOTCONN: * Transport endpoint is not connected
*/
J1939_XTP_ABORT_BAD_SEQ = 7, /* Bad sequence number (and software is not able to recover) * * EILSEQ: * Illegal byte sequence
*/
J1939_XTP_ABORT_DUP_SEQ = 8, /* Duplicate sequence number (and software is not able to * recover)
*/
/* helpers */ staticconstchar *j1939_xtp_abort_to_str(enum j1939_xtp_abort abort)
{ switch (abort) { case J1939_XTP_ABORT_BUSY: return"Already in one or more connection managed sessions and cannot support another."; case J1939_XTP_ABORT_RESOURCE: return"System resources were needed for another task so this connection managed session was terminated."; case J1939_XTP_ABORT_TIMEOUT: return"A timeout occurred and this is the connection abort to close the session."; case J1939_XTP_ABORT_GENERIC: return"CTS messages received when data transfer is in progress"; case J1939_XTP_ABORT_FAULT: return"Maximal retransmit request limit reached"; case J1939_XTP_ABORT_UNEXPECTED_DATA: return"Unexpected data transfer packet"; case J1939_XTP_ABORT_BAD_SEQ: return"Bad sequence number (and software is not able to recover)"; case J1939_XTP_ABORT_DUP_SEQ: return"Duplicate sequence number (and software is not able to recover)"; case J1939_XTP_ABORT_EDPO_UNEXPECTED: return"Unexpected EDPO packet (ETP) or Message size > 1785 bytes (TP)"; case J1939_XTP_ABORT_BAD_EDPO_PGN: return"Unexpected EDPO PGN (PGN in EDPO is bad)"; case J1939_XTP_ABORT_EDPO_OUTOF_CTS: return"EDPO number of packets is greater than CTS"; case J1939_XTP_ABORT_BAD_EDPO_OFFSET: return"Bad EDPO offset"; case J1939_XTP_ABORT_OTHER_DEPRECATED: return"Deprecated. Use 250 instead (Any other reason)"; case J1939_XTP_ABORT_ECTS_UNXPECTED_PGN: return"Unexpected ECTS PGN (PGN in ECTS is bad)"; case J1939_XTP_ABORT_ECTS_TOO_BIG: return"ECTS requested packets exceeds message size"; case J1939_XTP_ABORT_OTHER: return"Any other reason (if a Connection Abort reason is identified that is not listed in the table use code 250)"; default: return"";
}
}
staticint j1939_xtp_abort_to_errno(struct j1939_priv *priv, enum j1939_xtp_abort abort)
{ int err;
switch (abort) { case J1939_XTP_NO_ABORT:
WARN_ON_ONCE(abort == J1939_XTP_NO_ABORT);
err = 0; break; case J1939_XTP_ABORT_BUSY:
err = EALREADY; break; case J1939_XTP_ABORT_RESOURCE:
err = EMSGSIZE; break; case J1939_XTP_ABORT_TIMEOUT:
err = EHOSTUNREACH; break; case J1939_XTP_ABORT_GENERIC:
err = EBADMSG; break; case J1939_XTP_ABORT_FAULT:
err = ENOTRECOVERABLE; break; case J1939_XTP_ABORT_UNEXPECTED_DATA:
err = ENOTCONN; break; case J1939_XTP_ABORT_BAD_SEQ:
err = EILSEQ; break; case J1939_XTP_ABORT_DUP_SEQ:
err = EPROTO; break; case J1939_XTP_ABORT_EDPO_UNEXPECTED:
err = EPROTO; break; case J1939_XTP_ABORT_BAD_EDPO_PGN:
err = EPROTO; break; case J1939_XTP_ABORT_EDPO_OUTOF_CTS:
err = EPROTO; break; case J1939_XTP_ABORT_BAD_EDPO_OFFSET:
err = EPROTO; break; case J1939_XTP_ABORT_OTHER_DEPRECATED:
err = EPROTO; break; case J1939_XTP_ABORT_ECTS_UNXPECTED_PGN:
err = EPROTO; break; case J1939_XTP_ABORT_ECTS_TOO_BIG:
err = EPROTO; break; case J1939_XTP_ABORT_OTHER:
err = EPROTO; break; default:
netdev_warn(priv->ndev, "Unknown abort code %i", abort);
err = EPROTO;
}
if ((do_skcb->offset + do_skb->len) < offset_start) {
__skb_unlink(do_skb, &session->skb_queue); /* drop ref taken in j1939_session_skb_queue() */
skb_unref(do_skb);
spin_unlock_irqrestore(&session->skb_queue.lock, flags);
if (!skb)
netdev_dbg(priv->ndev, "%s: 0x%p: no skb found for start: %i, queue size: %i\n",
__func__, session, offset_start,
skb_queue_len(&session->skb_queue));
/* see if we are receiver * returns 0 for broadcasts, although we will receive them
*/ staticinlineint j1939_tp_im_receiver(conststruct j1939_sk_buff_cb *skcb)
{ return skcb->flags & J1939_ECU_LOCAL_DST;
}
/* see if we are sender */ staticinlineint j1939_tp_im_transmitter(conststruct j1939_sk_buff_cb *skcb)
{ return skcb->flags & J1939_ECU_LOCAL_SRC;
}
/* see if we are involved as either receiver or transmitter */ staticint j1939_tp_im_involved(conststruct j1939_sk_buff_cb *skcb, bool swap)
{ if (swap) return j1939_tp_im_receiver(skcb); else return j1939_tp_im_transmitter(skcb);
}
/* find existing session: * reverse: swap cb's src & dst * there is no problem with matching broadcasts, since * broadcasts (no dst, no da) would never call this * with reverse == true
*/ staticbool j1939_session_match(struct j1939_addr *se_addr, struct j1939_addr *sk_addr, bool reverse)
{ if (se_addr->type != sk_addr->type) returnfalse;
if (reverse) { if (se_addr->src_name) { if (se_addr->src_name != sk_addr->dst_name) returnfalse;
} elseif (se_addr->sa != sk_addr->da) { returnfalse;
}
if (se_addr->dst_name) { if (se_addr->dst_name != sk_addr->src_name) returnfalse;
} elseif (se_addr->da != sk_addr->sa) { returnfalse;
}
} else { if (se_addr->src_name) { if (se_addr->src_name != sk_addr->src_name) returnfalse;
} elseif (se_addr->sa != sk_addr->sa) { returnfalse;
}
if (se_addr->dst_name) { if (se_addr->dst_name != sk_addr->dst_name) returnfalse;
} elseif (se_addr->da != sk_addr->da) { returnfalse;
}
}
if (pkt_done)
j1939_tp_set_rxtimeout(session, 250);
out_free: if (ret)
kfree_skb(se_skb); else
consume_skb(se_skb);
return ret;
}
staticint j1939_xtp_txnext_transmiter(struct j1939_session *session)
{ struct j1939_priv *priv = session->priv; int ret = 0;
if (!j1939_tp_im_transmitter(&session->skcb)) {
netdev_alert(priv->ndev, "%s: 0x%p: called by not transmitter!\n",
__func__, session); return -EINVAL;
}
switch (session->last_cmd) { case 0:
ret = j1939_session_tx_rts(session); break;
case J1939_ETP_CMD_CTS: if (session->last_txcmd != J1939_ETP_CMD_DPO) {
ret = j1939_session_tx_dpo(session); if (ret) return ret;
}
fallthrough; case J1939_TP_CMD_CTS: case 0xff: /* did some data */ case J1939_ETP_CMD_DPO: case J1939_TP_CMD_BAM:
ret = j1939_session_tx_dat(session);
staticint j1939_xtp_txnext_receiver(struct j1939_session *session)
{ struct j1939_priv *priv = session->priv; int ret = 0;
if (!j1939_tp_im_receiver(&session->skcb)) {
netdev_alert(priv->ndev, "%s: 0x%p: called by not receiver!\n",
__func__, session); return -EINVAL;
}
switch (session->last_cmd) { case J1939_TP_CMD_RTS: case J1939_ETP_CMD_RTS:
ret = j1939_session_tx_cts(session); break;
case J1939_ETP_CMD_CTS: case J1939_TP_CMD_CTS: case 0xff: /* did some data */ case J1939_ETP_CMD_DPO: if ((session->skcb.addr.type == J1939_TP &&
j1939_cb_is_broadcast(&session->skcb))) break;
if (session->skcb.addr.type == J1939_SIMPLE) {
ret = j1939_simple_txnext(session);
} else { if (session->transmission)
ret = j1939_xtp_txnext_transmiter(session); else
ret = j1939_xtp_txnext_receiver(session);
}
switch (ret) { case -ENOBUFS: /* Retry limit is currently arbitrary chosen */ if (session->tx_retry < J1939_XTP_TX_RETRY_LIMIT) {
session->tx_retry++;
j1939_tp_schedule_txtimer(session,
10 + get_random_u32_below(16));
} else {
netdev_alert(priv->ndev, "%s: 0x%p: tx retry count reached\n",
__func__, session);
session->err = -ENETUNREACH;
j1939_session_rxtimer_cancel(session);
j1939_session_deactivate_activate_next(session);
} break; case -ENETDOWN: /* In this case we should get a netdev_event(), all active * sessions will be cleared by j1939_cancel_active_session(). * So handle this as an error, but let * j1939_cancel_active_session() do the cleanup including * propagation of the error to user space.
*/ break; case -EOVERFLOW:
j1939_session_cancel(session, J1939_XTP_ABORT_ECTS_TOO_BIG); break; case 0:
session->tx_retry = 0; break; default:
netdev_alert(priv->ndev, "%s: 0x%p: tx aborted with unknown reason: %i\n",
__func__, session, ret); if (session->skcb.addr.type != J1939_SIMPLE) {
j1939_session_cancel(session, J1939_XTP_ABORT_OTHER);
} else {
session->err = ret;
j1939_session_rxtimer_cancel(session);
j1939_session_deactivate_activate_next(session);
}
}
/* The message is probably stuck in the CAN controller and can * be send as soon as CAN bus is in working state again.
*/
session->err = -ETIME;
j1939_session_deactivate(session);
} else {
j1939_session_list_lock(session->priv); if (session->state >= J1939_SESSION_ACTIVE &&
session->state < J1939_SESSION_ACTIVE_MAX) {
netdev_alert(priv->ndev, "%s: 0x%p: rx timeout, send abort\n",
__func__, session);
j1939_session_get(session);
hrtimer_start(&session->rxtimer,
ms_to_ktime(J1939_XTP_ABORT_TIMEOUT_MS),
HRTIMER_MODE_REL_SOFT);
__j1939_session_cancel(session, J1939_XTP_ABORT_TIMEOUT);
}
j1939_session_list_unlock(session->priv);
if (!session->sk)
j1939_sk_errqueue(session, J1939_ERRQUEUE_RX_ABORT);
}
if (!pkt) goto out_session_cancel; elseif (dat[1] > session->pkt.block /* 0xff for etp */) goto out_session_cancel;
/* set packet counters only when not CTS(0) */
session->pkt.tx_acked = pkt - 1;
j1939_session_skb_drop_old(session);
session->pkt.last = session->pkt.tx_acked + dat[1]; if (session->pkt.last > session->pkt.total) /* safety measure */
session->pkt.last = session->pkt.total; /* TODO: do not set tx here, do it in txtimer */
session->pkt.tx = session->pkt.tx_acked;
session->last_cmd = dat[0]; if (dat[1]) {
j1939_tp_set_rxtimeout(session, 1250); if (session->transmission) { if (session->pkt.tx_acked)
j1939_sk_errqueue(session,
J1939_ERRQUEUE_TX_SCHED);
j1939_session_txtimer_cancel(session);
j1939_tp_schedule_txtimer(session, 0);
}
} else { /* CTS(0) */
j1939_tp_set_rxtimeout(session, 550);
} return;
ret = j1939_session_activate(session); if (ret) { /* Entering this scope indicates an issue with the J1939 bus. * Possible scenarios include: * - A time lapse occurred, and a new session was initiated * due to another packet being sent correctly. This could * have been caused by too long interrupt, debugger, or being * out-scheduled by another task. * - The bus is receiving numerous erroneous packets, either * from a malfunctioning device or during a test scenario.
*/
netdev_alert(priv->ndev, "%s: 0x%p: concurrent session with same addr (%02x %02x) is already active.\n",
__func__, session, skcb.addr.sa, skcb.addr.da);
j1939_session_put(session); return NULL;
}
if (!session->transmission) { if (j1939_xtp_rx_cmd_bad_pgn(session, skb)) return -EBUSY;
/* RTS on active session */
j1939_session_timers_cancel(session);
j1939_session_cancel(session, J1939_XTP_ABORT_BUSY);
}
if (session->last_cmd != 0) { /* we received a second rts on the same connection */
netdev_alert(priv->ndev, "%s: 0x%p: connection exists (%02x %02x). last cmd: %x\n",
__func__, session, skcb->addr.sa, skcb->addr.da,
session->last_cmd);
j1939_session_timers_cancel(session);
j1939_session_cancel(session, J1939_XTP_ABORT_BUSY); if (session->transmission)
j1939_session_deactivate_activate_next(session);
return -EBUSY;
}
if (session->skcb.addr.sa != skcb->addr.sa ||
session->skcb.addr.da != skcb->addr.da)
netdev_warn(priv->ndev, "%s: 0x%p: session->skcb.addr.sa=0x%02x skcb->addr.sa=0x%02x session->skcb.addr.da=0x%02x skcb->addr.da=0x%02x\n",
__func__, session,
session->skcb.addr.sa, skcb->addr.sa,
session->skcb.addr.da, skcb->addr.da); /* make sure 'sa' & 'da' are correct ! * They may be 'not filled in yet' for sending * skb's, since they did not pass the Address Claim ever.
*/
session->skcb.addr.sa = skcb->addr.sa;
session->skcb.addr.da = skcb->addr.da;
if (!session) { if (transmitter) { /* If we're the transmitter and this function is called, * we received our own RTS. A session has already been * created. * * For some reasons however it might have been destroyed * already. So don't create a new one here (using * "j1939_xtp_rx_rts_session_new()") as this will be a * receiver session. * * The reasons the session is already destroyed might * be: * - user space closed socket was and the session was * aborted * - session was aborted due to external abort message
*/ return;
}
session = j1939_xtp_rx_rts_session_new(priv, skb); if (!session) { if (cmd == J1939_TP_CMD_BAM && j1939_sk_recv_match(priv, skcb))
netdev_info(priv->ndev, "%s: failed to create TP BAM session\n",
__func__); return;
}
} else { if (j1939_xtp_rx_rts_session_active(session, skb)) {
j1939_session_put(session); return;
}
}
session->last_cmd = cmd;
if (cmd == J1939_TP_CMD_BAM) { if (!session->transmission)
j1939_tp_set_rxtimeout(session, 750);
} else { if (!session->transmission) {
j1939_session_txtimer_cancel(session);
j1939_tp_schedule_txtimer(session, 0);
}
j1939_tp_set_rxtimeout(session, 1250);
}
skcb = j1939_skb_to_cb(skb);
dat = skb->data; if (skb->len != 8) { /* makes no sense */
abort = J1939_XTP_ABORT_UNEXPECTED_DATA; goto out_session_cancel;
}
switch (session->last_cmd) { case 0xff: break; case J1939_ETP_CMD_DPO: if (skcb->addr.type == J1939_ETP) break;
fallthrough; case J1939_TP_CMD_BAM:
fallthrough; case J1939_TP_CMD_CTS: if (skcb->addr.type != J1939_ETP) break;
fallthrough; default:
netdev_info(priv->ndev, "%s: 0x%p: last %02x\n", __func__,
session, session->last_cmd); goto out_session_cancel;
}
packet = (dat[0] - 1 + session->pkt.dpo); if (packet > session->pkt.total ||
(session->pkt.rx + 1) > session->pkt.total) {
netdev_info(priv->ndev, "%s: 0x%p: should have been completed\n",
__func__, session); goto out_session_cancel;
}
se_skb = j1939_session_skb_get_by_offset(session, packet * 7); if (!se_skb) {
netdev_warn(priv->ndev, "%s: 0x%p: no skb found\n", __func__,
session); goto out_session_cancel;
}
tpdat = se_skb->data; if (!session->transmission) {
memcpy(&tpdat[offset], &dat[1], nbytes);
} else { int err;
err = memcmp(&tpdat[offset], &dat[1], nbytes); if (err)
netdev_err_once(priv->ndev, "%s: 0x%p: Data of RX-looped back packet (%*ph) doesn't match TX data (%*ph)!\n",
__func__, session,
nbytes, &dat[1],
nbytes, &tpdat[offset]);
}
if (packet == session->pkt.rx)
session->pkt.rx++;
if (se_skcb->addr.type != J1939_ETP &&
j1939_cb_is_broadcast(&session->skcb)) { if (session->pkt.rx >= session->pkt.total)
final = true; else
remain = true;
} else { /* never final, an EOMA must follow */ if (session->pkt.rx >= session->pkt.last)
do_cts_eoma = true;
}
if (skcb->addr.type == J1939_ETP &&
j1939_cb_is_broadcast(skcb)) return ERR_PTR(-EDESTADDRREQ);
/* fill in addresses from names */
ret = j1939_ac_fixup(priv, skb); if (unlikely(ret)) return ERR_PTR(ret);
/* fix DST flags, it may be used there soon */ if (j1939_address_is_unicast(skcb->addr.da) &&
priv->ents[skcb->addr.da].nusers)
skcb->flags |= J1939_ECU_LOCAL_DST;
if (!j1939_tp_im_involved_anydir(skcb) && !j1939_cb_is_broadcast(skcb)) return 0;
switch (skcb->addr.pgn) { case J1939_ETP_PGN_DAT:
skcb->addr.type = J1939_ETP;
fallthrough; case J1939_TP_PGN_DAT:
j1939_xtp_rx_dat(priv, skb); break;
case J1939_ETP_PGN_CTL:
skcb->addr.type = J1939_ETP;
fallthrough; case J1939_TP_PGN_CTL: if (skb->len < 8) return 0; /* Don't care. Nothing to extract here */
j1939_tp_cmd_recv(priv, skb); break; default: return 0; /* no problem */
} return 1; /* "I processed the message" */
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.