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_family
和can_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
:流控制帧中提供的最大等待帧数。
链路层选项¶
链路层 (LL) 选项可以使用 CAN_ISOTP_LL_OPTS
选项名称传递。
struct can_isotp_ll_options ll_opts;
ret = setsockopt(s, SOL_CAN_ISOTP, CAN_ISOTP_LL_OPTS, &ll_opts, sizeof(ll_opts));
其中 can_isotp_ll_options
结构包含以下内容:
struct can_isotp_ll_options {
u8 mtu;
u8 tx_dl;
u8 tx_flags;
};
mtu
:生成和接受的 CAN 帧类型,可以等于经典 CAN 帧的CAN_MTU
或 CAN FD 帧的CANFD_MTU
。tx_dl
:传输帧的最大有效负载长度,可以具有以下值之一:8、12、16、20、24、32、48、64。大于 8 的值仅适用于 CAN FD 流量(即:mtu = CANFD_MTU
)。tx_flags
:在创建帧时设置到struct canfd_frame.flags
中的标志。仅适用于 CAN FD 流量(即:mtu = CANFD_MTU
)。
传输 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