ISO 15765-2 (ISO-TP)

概述

ISO 15765-2,也称为ISO-TP,是一种专门为CAN上的诊断通信定义的传输协议。它广泛应用于汽车工业,例如作为UDSonCAN(ISO 14229-3)或排放相关诊断服务(ISO 15031-5)的传输协议。

ISO-TP可用于基于CAN CC(又名经典CAN)和CAN FD(具有灵活数据速率的CAN)的网络。它也被设计为与使用SAE J1939作为数据链路层的CAN网络兼容(但这并非强制要求)。

使用的规范

  • ISO 15765-2:2024 : 道路车辆 - 基于控制器局域网 (DoCAN) 的诊断通信。第 2 部分:传输协议和网络层服务。

寻址

在其最简单的形式中,ISO-TP基于连接到同一网络的节点的两种寻址模式

  • 物理寻址由两个节点特定的地址实现,用于1对1通信。

  • 功能寻址由一个节点特定的地址实现,用于1对N通信。

可以采用三种不同的寻址格式

  • “normal”:每个地址都简单地由一个CAN ID表示。

  • “extended”:每个地址由一个CAN ID加上CAN有效负载的第一个字节表示;两个地址之间的CAN ID和有效负载内的字节都应该不同。

  • “mixed”:每个地址由一个CAN ID加上CAN有效负载的第一个字节表示;两个地址之间的CAN ID不同,但附加字节相同。

传输协议和相关帧类型

当使用ISO-TP协议传输数据时,有效负载可以容纳在一个CAN消息中,也可以不容纳,还要考虑到协议产生的开销和可选的扩展寻址。在第一种情况下,数据使用所谓的单帧(SF)一次性传输。在第二种情况下,ISO-TP定义了一个多帧协议,其中发送方提供(通过第一帧 - FF)要传输的PDU长度,并请求流控制(FC)帧,该帧提供宏数据块的最大支持大小(blocksize)以及组成该块的单个CAN消息之间的最短时间(stmin)。一旦收到此信息,发送方开始发送包含数据有效负载片段的帧(称为连续帧 - CF),并在每个blocksize大小的块后停止,以等待来自接收方的确认,接收方应发送另一个流控制帧,以通知发送方其接收更多数据的可用性。

如何使用ISO-TP

与其他CAN协议一样,ISO-TP堆栈支持内置于用于CAN总线的Linux网络子系统,又名。Linux-CAN或SocketCAN,因此遵循相同的套接字API。

创建和基本使用ISO-TP套接字

要使用ISO-TP堆栈,应使用#include <linux/can/isotp.h>。然后可以使用PF_CAN协议族、SOCK_DGRAM类型(因为底层协议在设计上是基于数据报的)和CAN_ISOTP协议来创建套接字

s = socket(PF_CAN, SOCK_DGRAM, CAN_ISOTP);

成功创建套接字后,应调用bind(2)将套接字绑定到所需的CAN接口;为此

  • 应在提供给调用的sockaddr中指定TX CAN ID。

  • 还应指定RX CAN ID,除非已通过套接字选项设置了广播标志(如下所述)。

绑定到接口后,可以使用常用的read(2)write(2)系统调用以及send(2)sendmsg(2)recv(2)recvmsg(2)来读取和写入套接字。与CAN_RAW套接字API不同,只有ISO-TP数据字段(实际有效负载)由用户空间应用程序使用这些调用发送和接收。地址信息和协议信息由ISO-TP堆栈使用套接字创建期间提供的配置自动填充。同样,堆栈将在需要时使用传输机制(即,当数据有效负载的大小超过底层CAN总线的MTU时)。

用于SocketCAN的sockaddr结构具有用于ISO-TP的扩展,如下所示

struct sockaddr_can {
    sa_family_t can_family;
    int         can_ifindex;
    union {
        struct { canid_t rx_id, tx_id; } tp;
    ...
    } can_addr;
}
  • can_familycan_ifindex与用于其他SocketCAN套接字的目的相同。

  • can_addr.tp.rx_id指定接收(RX)CAN ID,并将用作RX过滤器。

  • can_addr.tp.tx_id指定发送(TX)CAN ID

ISO-TP套接字选项

创建ISO-TP套接字时,会设置合理的默认值。可以使用setsockopt(2)修改某些选项,和/或使用getsockopt(2)读取这些选项。

常规选项

可以使用CAN_ISOTP_OPTS optname传递常规套接字选项

struct can_isotp_options opts;
ret = setsockopt(s, SOL_CAN_ISOTP, CAN_ISOTP_OPTS, &opts, sizeof(opts))

其中can_isotp_options结构具有以下内容

struct can_isotp_options {
    u32 flags;
    u32 frame_txtime;
    u8  ext_address;
    u8  txpad_content;
    u8  rxpad_content;
    u8  rx_ext_address;
};
  • flags:应用于ISO-TP堆栈默认行为的修饰符。以下标志可用

    • CAN_ISOTP_LISTEN_MODE:仅侦听(不发送FC帧);通常用作测试功能。

    • CAN_ISOTP_EXTEND_ADDR:使用ext_address中指定的字节作为附加地址组件。如果单独使用,这将启用“混合”寻址格式;如果与CAN_ISOTP_RX_EXT_ADDR结合使用,则启用“扩展”寻址格式。

    • CAN_ISOTP_TX_PADDING:启用发送帧的填充,使用txpad_content作为填充字节的值。

    • CAN_ISOTP_RX_PADDING:启用接收帧的填充,使用rxpad_content作为填充字节的值。

    • CAN_ISOTP_CHK_PAD_LEN:检查接收帧上正确的填充长度。

    • CAN_ISOTP_CHK_PAD_DATA:根据rxpad_content检查接收帧上的填充字节;如果未指定CAN_ISOTP_RX_PADDING,则忽略此标志。

    • CAN_ISOTP_HALF_DUPLEX:强制ISO-TP套接字进入半双工模式(即,传输机制只能是同时传入或传出,而不能两者兼有)。

    • CAN_ISOTP_FORCE_TXSTMIN:忽略来自接收到的FC的stmin;通常用作测试功能。

    • CAN_ISOTP_FORCE_RXSTMIN:忽略取决于rx stmin的CF;通常用作测试功能。

    • CAN_ISOTP_RX_EXT_ADDR:在接收路径上使用rx_ext_address而不是ext_address作为扩展寻址字节。如果与CAN_ISOTP_EXTEND_ADDR结合使用,此标志有效地启用“扩展”寻址格式。

    • CAN_ISOTP_WAIT_TX_DONE:在从write(2)send(2)调用返回之前,等待直到发送完帧(即,阻塞写入操作)。

    • CAN_ISOTP_SF_BROADCAST:使用1对N功能寻址(不能与CAN_ISOTP_CF_BROADCAST一起指定)。

    • CAN_ISOTP_CF_BROADCAST:使用1对N传输,无需流量控制(不能与CAN_ISOTP_SF_BROADCAST一起指定)。注意:这不在ISO 15765-2标准范围内。

    • CAN_ISOTP_DYN_FC_PARMS:启用流控制参数的动态更新。

  • frame_txtime:帧传输时间(在ISO标准中定义为N_As/N_Ar);如果0,则使用默认值(或最后设置的值)。要将传输时间设置为0,应使用CAN_ISOTP_FRAME_TXTIME_ZERO宏(等于0xFFFFFFFF)。

  • ext_address:扩展寻址字节,如果指定了CAN_ISOTP_EXTEND_ADDR标志,则使用该字节。

  • txpad_content:用作发送帧的填充值的字节。

  • rxpad_content:用作接收帧的填充值的字节。

  • rx_ext_address:接收路径的扩展寻址字节,如果指定了CAN_ISOTP_RX_EXT_ADDR标志,则使用该字节。

流控制选项

可以使用CAN_ISOTP_RECV_FC optname传递流控制(FC)选项,以提供用于接收ISO-TP PDU的通信参数。

struct can_isotp_fc_options fc_opts;
ret = setsockopt(s, SOL_CAN_ISOTP, CAN_ISOTP_RECV_FC, &fc_opts, sizeof(fc_opts));

其中can_isotp_fc_options结构具有以下内容

struct can_isotp_options {
    u8 bs;
    u8 stmin;
    u8 wftmax;
};
  • bs:在流控制帧中提供的块大小。

  • stmin:在流控制帧中提供的最小间隔时间;可以具有以下值(其他值已保留)

    • 0x00 - 0x7F : 0 - 127 毫秒

    • 0xF1 - 0xF9 : 100 微秒 - 900 微秒

  • wftmax:在流控制帧中提供的最大等待帧数。

传输 stmin

可以使用CAN_ISOTP_TX_STMIN optname强制传输最小间隔时间(stmin),并以32位无符号整数的形式提供以微秒为单位的stmin值;这将覆盖接收方在流控制帧中发送的值

uint32_t stmin;
ret = setsockopt(s, SOL_CAN_ISOTP, CAN_ISOTP_TX_STMIN, &stmin, sizeof(stmin));

接收 stmin

可以使用CAN_ISOTP_RX_STMIN optname强制接收最小间隔时间(stmin),并以32位无符号整数的形式提供以微秒为单位的stmin值;接收到的连续帧(CF)的时间戳差异小于该值将被忽略

uint32_t stmin;
ret = setsockopt(s, SOL_CAN_ISOTP, CAN_ISOTP_RX_STMIN, &stmin, sizeof(stmin));

多帧传输支持

Linux内核中包含的ISO-TP堆栈支持标准定义的多帧传输机制,但具有以下约束

  • PDU的最大大小由模块参数定义,在构建时施加了硬性限制。

  • 当传输正在进行时,后续对write(2)的调用将阻塞,而对send(2)的调用将阻塞或失败,具体取决于是否存在MSG_DONTWAIT标志。

  • 不支持发送“等待帧”:PDU是否可以完全接收是在收到第一帧时决定的。

错误

以下错误会报告给用户空间

RX路径错误

-ETIMEDOUT

数据接收超时

-EILSEQ

多帧接收期间的序列号不匹配

-EBADMSG

数据接收填充错误

TX路径错误

-ECOMM

流控制接收超时

-EMSGSIZE

流控制接收溢出

-EBADMSG

流控制接收布局/填充错误

示例

基本节点示例

以下示例使用“normal”物理寻址实现一个节点,RX ID等于0x18DAF142,TX ID等于0x18DA42F1。所有选项都保留为其默认值。

int s;
struct sockaddr_can addr;
int ret;

s = socket(PF_CAN, SOCK_DGRAM, CAN_ISOTP);
if (s < 0)
    exit(1);

addr.can_family = AF_CAN;
addr.can_ifindex = if_nametoindex("can0");
addr.can_addr.tp.tx_id = 0x18DA42F1 | CAN_EFF_FLAG;
addr.can_addr.tp.rx_id = 0x18DAF142 | CAN_EFF_FLAG;

ret = bind(s, (struct sockaddr *)&addr, sizeof(addr));
if (ret < 0)
    exit(1);

/* Data can now be received using read(s, ...) and sent using write(s, ...) */

更多示例

更完整(和复杂)的示例可以在isotp*用户空间工具中找到,这些工具作为can-utils实用程序的一部分分发,网址为:https://github.com/linux-can/can-utils