ISO 15765-2 (ISO-TP)

概述

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

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

使用的规范

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

寻址

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

  • 物理寻址通过两个节点特定的地址实现,并在 1 对 1 通信中使用。

  • 功能寻址通过一个节点特定的地址实现,并在 1 对 N 通信中使用。

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

  • “正常”:每个地址仅由一个 CAN ID 表示。

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

  • “混合”:每个地址由一个 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 接口;要做到这一点

  • 一个 TX CAN ID 应作为提供给调用本身的 sockaddr 的一部分指定。

  • 还应指定一个 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 选项名称并以 32 位无符号整数的形式提供微秒级的 stmin 值来强制指定最小传输间隔时间 (stmin);这将覆盖接收器在流控制帧中发送的值。

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

接收 stmin

可以使用 CAN_ISOTP_RX_STMIN 选项名称并以 32 位无符号整数的形式提供微秒级的 stmin 值来强制指定最小接收间隔时间 (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

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

示例

基本节点示例

以下示例使用“正常”物理寻址实现一个节点,其 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.tp.tx_id = 0x18DA42F1 | CAN_EFF_FLAG;
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