SocketCAN — 控制器局域网套接字实现深度解析
第一部分:设计理念与核心概念
1. 概述:SocketCAN 是什么
SocketCAN 是 Linux 下对控制器局域网 (Controller Area Network) 协议的原生实现,它将 CAN 设备抽象为网络接口,将 CAN 通信抽象为套接字通信,使 CAN 总线操作与传统的 TCP/IP 网络编程模型保持一致。
/** * @brief SocketCAN 的核心设计目标 * * - 遵循 Berkeley 套接字 API,降低学习曲线 * - 利用 Linux 网络协议栈,复用队列、过滤等基础设施 * - 支持多个应用程序同时访问同一 CAN 接口 * - 支持 CAN 原始帧和多种高层传输协议 (如 ISO-TP) * - 提供内核空间的广播管理器和高效的接收列表 */
2. 动机:为什么弃用字符设备而选择 Socket API
在 SocketCAN 之前,Linux 上已存在基于字符设备的 CAN 驱动,但它们存在诸多限制。内核文档列举了使用字符设备的不优雅之处,以及采用网络栈的自然性。
传统字符设备方案的缺陷:
| 问题 | 字符设备表现 | SocketCAN 解决方案 |
|---|---|---|
| 使用复杂性 | 必须通过 ioctl() 完成所有配置(协议选择、接口绑定、过滤规则等),代码冗长且易出错 |
使用标准的 socket()、bind()、setsockopt() 语义,逻辑清晰 |
| 代码重复 | 无法复用内核网络队列,每个驱动都要实现帧缓冲、多路分解、流量控制等 | 直接借用 Linux 网络协议栈的 sk_buff 队列和流量控制机制 |
| 抽象缺失 | 驱动直接提供字符设备给用户空间,不符合 Unix “策略与机制分离” 的设计哲学 | 通过协议族 PF_CAN 提供统一接口,硬件驱动仅实现底层操作,类似 TTY 层或音频子系统 |
| 多用户支持 | 通常只允许一个进程打开设备(类似串口),无法实现多个应用同时监听不同 CAN-ID | 多个套接字可绑定到同一接口,内核通过高效过滤列表将帧分发到各个套接字 |
/** * @brief SocketCAN 采用网络栈的必然性 * * 实现完整的 CAN 字符设备子系统需要: * - 注册特定 CAN-ID 的机制 * - 多个文件描述符的支持 * - 帧的多路分解和复用 * - 复杂的排队机制 * - 设备驱动的统一接口 * * 而这些恰恰是 Linux 网络栈已经提供的能力。因此, * 将 CAN 实现为网络协议族是最自然、最高效的选择。 * @see net/can/af_can.c */
调试场景:如果迁移一个旧版字符设备 CAN 应用到 SocketCAN,常见的问题是忘记替换 ioctl 调用为 setsockopt。一个典型症状是应用能够发送帧,但无法接收,因为接收过滤没有正确设置。
3. SocketCAN 核心概念
3.1 为什么 CAN-ID 可视为源地址
CAN 总线是广播介质,没有类似以太网的 MAC 地址。CAN 标识符 (CAN-ID) 用于总线仲裁,决定哪个节点优先发送。在良好设计的 CAN 网络中,每个 CAN-ID 由特定 ECU 唯一发送。因此,CAN-ID 可被视为“源地址”。这一设计哲学贯穿 SocketCAN:套接字通过绑定 CAN-ID(或 ID 范围)来声明自己感兴趣的数据流,而不是绑定到一个目标地址。
3.2 接收列表 (Receive Lists)
多个应用程序可能对来自同一 CAN 接口的相同 CAN-ID 感兴趣。SocketCAN 核心通过高效的接收列表来解决这一需求。
/** * @brief 接收列表的分级设计 * * can_rx_register() / can_rx_unregister() 允许协议模块订阅 CAN-ID。 * 为了运行时 CPU 效率,接收列表按设备和过滤复杂度分为: * - rcvlist_all: 未过滤的条目 * - rcvlist_eff: 单个扩展帧 (EFF) 条目 * - rcvlist_err: 错误消息帧掩码 * - rcvlist_fil: 掩码/值过滤器 * - rcvlist_inv: 反向掩码/值过滤器 * - rcvlist_sff: 单个标准帧 (SFF) 条目 * * 这种划分使内核在接收路径上能以 O(1) 或 O(log n) 的代价 * 找到目标套接字,而不是遍历全部订阅者。 * @see /proc/net/can/rcvlist_* */
调试技巧:当怀疑某个套接字没有收到预期帧时,检查 /proc/net/can/rcvlist_* 可验证过滤规则是否正确注册。matches 计数器显示实际命中的帧数。
3.3 本地回环 (Local Loopback)
为了保证同一节点上应用间的通信行为与跨节点通信一致,SocketCAN 默认启用本地回环:当一个套接字发送 CAN 帧时,该帧会像从总线上收到一样递送给本机其他监听该 CAN-ID 的套接字。
/** * @brief 回环的两种实现模式 * * 1. 控制器硬件回环:CAN 控制器在发送成功后自动将帧回送到接收队列。 * 此时设备驱动应设置 IFF_ECHO 标志,通知核心不需要软件回环。 * 2. 软件回环:当硬件不支持时,SocketCAN 核心在成功发送后模拟回环。 * * @note 默认情况下,发送套接字自身不会收到自己发送的帧(recv_own_msgs=0)。 * 这在多数场景下符合预期,但可通过 CAN_RAW_RECV_OWN_MSGS 选项改变。 */
3.4 网络问题通知 (Error Frames)
CAN 总线上的物理层和 MAC 层错误(如位错误、填充错误、总线关闭)对诊断至关重要。SocketCAN 允许用户空间通过标准的 CAN 过滤机制接收错误消息帧。
-
错误帧的格式定义在
include/uapi/linux/can/error.h。 -
默认情况下,接收错误帧是禁用的,需要显式通过
CAN_RAW_ERR_FILTER选项开启。 -
错误类型通过位掩码分类,
CAN_ERR_MASK可启用所有错误报告。
第二部分:编程接口与数据结构
4. 如何使用 SocketCAN
4.1 套接字创建与协议选择
SocketCAN 使用协议族 PF_CAN。目前提供两种套接字类型:
/** * @brief 创建 CAN 套接字 * * @param PF_CAN CAN 协议族 * @param SOCK_RAW 原始套接字,使用 CAN_RAW 协议 * @param CAN_RAW 协议编号,允许发送/接收原始 CAN 帧 * * @return 成功返回套接字描述符,失败返回 -1 */ int s = socket(PF_CAN, SOCK_RAW, CAN_RAW); /** * @brief 创建广播管理器套接字 * @param SOCK_DGRAM 数据报类型 * @param CAN_BCM 广播管理器协议 */ int s = socket(PF_CAN, SOCK_DGRAM, CAN_BCM);
4.2 绑定到接口
由于 CAN 没有 IP 地址,套接字需通过接口索引绑定到具体的 CAN 网络接口。
/** * @brief 绑定 CAN 套接字到接口 "can0" * * @note 使用 ioctl(SIOCGIFINDEX) 从接口名获取索引 * addr.can_ifindex = 0 表示绑定到所有接口 * 绑定到 'any' 时,需使用 recvfrom() 获取来源接口 */ struct sockaddr_can addr; struct ifreq ifr; strcpy(ifr.ifr_name, "can0"); ioctl(s, SIOCGIFINDEX, &ifr); addr.can_family = AF_CAN; addr.can_ifindex = ifr.ifr_ifindex; bind(s, (struct sockaddr *)&addr, sizeof(addr));
4.3 帧结构
SocketCAN 定义了两种帧结构:传统 CAN (struct can_frame) 和 CAN FD (struct canfd_frame)。
/**
* @brief 传统 CAN 帧结构 (CAN 2.0B)
*
* @field can_id 32位 CAN-ID,包含 EFF/RTR/ERR 标志
* @field len 实际负载长度 (0..8),应代替 can_dlc 使用
* @field can_dlc 已废弃,历史上命名不当,实际存的是长度而非 DLC
* @field len8_dlc 当 len==8 时可存储 DLC 值 9..15 (传统 CAN 控制器需要)
* @field data 负载数据,8字节对齐便于自定义结构体映射
*/
struct can_frame {
canid_t can_id;
union { __u8 len; __u8 can_dlc; };
__u8 __pad;
__u8 __res0;
__u8 len8_dlc;
__u8 data[8] __attribute__((aligned(8)));
};
/**
* @brief CAN FD 帧结构 (灵活数据速率)
*
* @field len 负载长度 (0..64),无歧义
* @field flags 附加标志 (如 CANFD_BRS, CANFD_ESI)
* @field data 64字节负载,8字节对齐
*
* @note struct canfd_frame 的前几个字段与 struct can_frame 兼容,
* 使得原有代码迁移时只需扩大缓冲区。
*/
struct canfd_frame {
canid_t can_id;
__u8 len;
__u8 flags;
__u8 __res0;
__u8 __res1;
__u8 data[64] __attribute__((aligned(8)));
};
场景调试:当发现接收的帧长度字段异常时,检查是否错误地使用了 can_dlc 而不是 len。在读取传统 CAN 帧时,len 直接给出有效字节数;在 CAN FD 场景下,永远不要假设 8 字节上限。
4.4 CAN FD 支持
CAN FD 的 MTU 不同 (CAN_MTU=16, CANFD_MTU=72)。要启用 CAN FD,必须在 RAW 套接字上设置 CAN_RAW_FD_FRAMES 选项。
/**
* @brief 启用 CAN FD 支持
*
* 设置后,套接字可发送和接收两种帧,应用程序通过 read() 返回的
* 字节数区分帧类型:
* - nbytes == CANFD_MTU (72) → 收到 CAN FD 帧
* - nbytes == CAN_MTU (16) → 收到传统 CAN 帧
*
* @note 旧内核不支持此选项时返回 -ENOPROTOOPT
*/
int enable = 1;
setsockopt(s, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &enable, sizeof(enable));
// 接收侧处理
struct canfd_frame cfd;
nbytes = read(s, &cfd, CANFD_MTU);
if (nbytes == CANFD_MTU) {
// CAN FD 帧, cfd.flags 有效
} else if (nbytes == CAN_MTU) {
// 传统 CAN 帧, cfd.flags 未定义
}
设计精髓:通过保持两种帧结构的兼容性,应用程序可以始终使用 struct canfd_frame 作为缓冲区,无需重构代码即可同时支持两种帧格式。len 字段在两种结构中偏移一致,意味着通过 cfd.len 总能获得正确长度。
5. RAW 协议套接字详解
RAW 套接字是使用最广泛的 CAN 接口,对应传统的字符设备操作模型,但提供了更丰富的控制选项。
5.1 默认行为
绑定 RAW 套接字时,系统设置以下默认值:
-
过滤器:一个“接收所有”的过滤器
-
错误帧:不接收
-
回环:启用
-
自身消息:不接收自己发送的帧
5.2 CAN 过滤器 (CAN_RAW_FILTER)
/**
* @brief CAN 过滤器结构
*
* 匹配条件: (received_can_id & can_mask) == (can_id & can_mask)
* 若 can_id 中设置 CAN_INV_FILTER 位,逻辑取反。
*
* 每个 RAW 套接字可独立设置 0..n 个过滤器。
* 设置零过滤器意味着“发送专用”,内核将移除该套接字的接收列表以节省 CPU。
*/
struct can_filter {
canid_t can_id;
canid_t can_mask;
};
/**
* @brief 优化提示:利用单 ID 索引
*
* 对于单 CAN-ID 的订阅,设置 CAN_SFF_MASK 或 CAN_EFF_MASK 时,
* 内核利用 ID 直接索引订阅列表(SFF)或使用哈希(EFF),
* 从而在接收路径上达到 O(1) 效率。
*
* 要获得优化,必须同时设置 CAN_EFF_FLAG 和 CAN_RTR_FLAG 在 mask 中,
* 以明确区分 SFF/EFF。
*
* @code
* struct can_filter rfilter[2];
* rfilter[0].can_id = 0x123;
* rfilter[0].can_mask = (CAN_EFF_FLAG | CAN_RTR_FLAG | CAN_SFF_MASK);
* rfilter[1].can_id = 0x12345678 | CAN_EFF_FLAG;
* rfilter[1].can_mask = (CAN_EFF_FLAG | CAN_RTR_FLAG | CAN_EFF_MASK);
* setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));
* @endcode
*/
5.3 其他 RAW 套接字选项
| 选项 | 用途 | 默认值 |
|---|---|---|
CAN_RAW_ERR_FILTER |
指定接收哪些类型的错误帧 | 无(禁用错误帧) |
CAN_RAW_LOOPBACK |
启用/禁用本地回环 | 1 (启用) |
CAN_RAW_RECV_OWN_MSGS |
是否接收自己发送的帧 | 0 (不接收) |
CAN_RAW_JOIN_FILTERS |
滤波器按逻辑与 (AND) 操作 | 多个滤波器为逻辑或 (OR) |
调试经验:如果发现发送的帧能在外部分析仪上看到,但本机其他套接字收不到,首先检查 CAN_RAW_LOOPBACK 是否被意外关闭。若需要在发送套接字上确认传输成功,可启用 CAN_RAW_RECV_OWN_MSGS,然后检查回环帧是否存在。
5.4 返回消息标志
当使用 recvmsg() 时,msg->msg_flags 可能包含:
-
MSG_DONTROUTE:帧来自本地主机 -
MSG_CONFIRM:帧是由该套接字发送的(传输确认),需要CAN_RAW_RECV_OWN_MSGS开启
第 6 章 广播管理器协议套接字 (BCM, SOCK_DGRAM)
广播管理器(BCM)是内核提供的一种高级 CAN 服务。与 RAW 套接字的“发送/接收原始帧”不同,BCM 支持在内核空间配置周期性发送任务和内容变化检测过滤器。这样可将定时和过滤逻辑从用户空间移到内核,减少上下文切换,提高实时性。
6.1 BCM 消息格式
BCM 使用专有消息结构 struct bcm_msg_head 与内核通信。用户空间通过 write() 发送配置指令,通过 read() 接收响应和通知。
/**
* @brief BCM 消息头结构
*
* @field opcode 操作码,定义要执行的操作
* @field flags 控制标志,影响操作行为
* @field count 发送次数(与 ival1 配合使用)
* @field ival1 第一个间隔时间(首次或计数期间的间隔)
* @field ival2 第二个间隔时间(count 用完后使用)
* @field can_id 此任务的唯一 CAN 标识符
* @field nframes 后续附加的 CAN 帧数量
* @field frames 柔性数组成员,存放实际的 CAN 帧
*
* @note BCM 套接字使用 connect() 而不是 bind() 关联到 CAN 接口。
* 绑定到 'any' 接口时,sendto() 可覆盖传出接口。
*/
struct bcm_msg_head {
__u32 opcode;
__u32 flags;
__u32 count;
struct timeval ival1, ival2;
canid_t can_id;
__u32 nframes;
struct can_frame frames[0];
};
6.2 BCM 操作码
BCM 的操作分为四类,完整覆盖了任务生命周期:
| 类别 | 操作码 | 方向 | 描述 |
|---|---|---|---|
| 发送配置 | TX_SETUP |
用户→内核 | 创建(周期性)发送任务 |
TX_DELETE |
用户→内核 | 删除发送任务(仅需 can_id) | |
TX_READ |
用户→内核 | 读取发送任务属性 | |
TX_SEND |
用户→内核 | 发送单个 CAN 帧 | |
| 发送响应 | TX_STATUS |
内核→用户 | 回复 TX_READ 请求 |
TX_EXPIRED |
内核→用户 | 通知计数完成 | |
| 接收配置 | RX_SETUP |
用户→内核 | 创建接收内容过滤订阅 |
RX_DELETE |
用户→内核 | 删除接收订阅 | |
RX_READ |
用户→内核 | 读取接收订阅属性 | |
| 接收响应 | RX_STATUS |
内核→用户 | 回复 RX_READ 请求 |
RX_TIMEOUT |
内核→用户 | 检测到消息超时 | |
RX_CHANGED |
内核→用户 | 检测到 CAN 帧内容变化 |
6.3 BCM 标志位
flags 字段控制 BCM 操作的细节行为:
| 标志 | 作用域 | 描述 |
|---|---|---|
SETTIMER |
TX/RX | 将 ival1/ival2/count 的值应用到定时器 |
STARTTIMER |
TX/RX | 立即启动定时器(同时发送第一帧) |
TX_COUNTEVT |
TX | count 耗尽时生成 TX_EXPIRED 通知 |
TX_ANNOUNCE |
TX | 数据变更时立即发送,不等定时器 |
TX_CP_CAN_ID |
TX | 将头部 can_id 复制到后续帧 |
RX_FILTER_ID |
RX | 仅按 can_id 过滤,不需要帧数据 |
RX_CHECK_DLC |
RX | DLC 变化时触发 RX_CHANGED |
RX_NO_AUTOTIMER |
RX | 不自动启动超时监控 |
RX_ANNOUNCE_RESUME |
RX | 超时后恢复接收时产生 RX_CHANGED |
TX_RESET_MULTI_IDX |
TX | 重置多帧发送索引 |
RX_RTR_FRAME |
RX | 回复 RTR 请求 |
CAN_FD_FRAME |
TX/RX | 指示后续帧为 CAN FD 格式 |
6.4 BCM 定时器模型
BCM 支持双定时器模型,用于实现灵活的多阶段周期性发送:
阶段 1 (count × ival1): 每隔 ival1 发送一次,共 count 次 ↓ 阶段 2 (ival2): count 用完后,每隔 ival2 发送一次
/**
* @brief BCM 定时器使用示例
*
* 创建每 100ms 发送 5 次,之后每 1s 发送一次的任务:
*
* struct {
* struct bcm_msg_head msg_head;
* struct can_frame frame;
* } msg;
*
* msg.msg_head.opcode = TX_SETUP;
* msg.msg_head.can_id = 0x123;
* msg.msg_head.flags = SETTIMER | STARTTIMER;
* msg.msg_head.count = 5;
* msg.msg_head.ival1 = {0, 100000}; // 100ms
* msg.msg_head.ival2 = {1, 0}; // 1s
* msg.msg_head.nframes = 1;
* // 填充 msg.frame ...
* write(s, &msg, sizeof(msg));
*/
场景调试:如果 BCM 任务没有按预期发送,首先检查是否正确设置了 STARTTIMER 标志。只设置 SETTIMER 不会启动发送,必须配合 STARTTIMER 一起使用。
6.5 BCM 多帧序列发送
TX_SETUP 可附带最多 256 个 CAN 帧,每次发送按索引递增,到达 nframes 后回绕。
6.6 BCM 接收过滤与多路复用
BCM 支持内容变化检测和超时监控:
-
ival1:若在ival1时间内未再次收到匹配消息,发送RX_TIMEOUT通知。 -
ival2:节流速率,将接收消息速率降至ival2的间隔。
多路复用过滤:对于包含多路选择器的消息序列,可传递超过 1 个 CAN 帧的数组。第一个帧作为位掩码,后续帧定义不同多路选择器值和对应的匹配掩码。最多支持 257 个帧(1 个 MUX 掩码 + 256 个过滤器)。
/** * @brief BCM 多路复用过滤示例 * * 以下配置定义了 4 个多路选择器值 (0x01, 0x02, 0x33, 0x4F) * 对应的匹配掩码。当收到 CAN 帧时,内核先根据首帧的 MUX 掩码 * 提取多路选择器值,再用对应的数据掩码与上次接收的帧比较, * 若变化则产生 RX_CHANGED 通知。 */ U64_DATA(&msg.frame[0]) = 0xFF00000000000000ULL; // MUX 掩码 U64_DATA(&msg.frame[1]) = 0x01000000000000FFULL; // MUX=0x01 的数据掩码 U64_DATA(&msg.frame[2]) = 0x0200FFFF000000FFULL; // MUX=0x02 U64_DATA(&msg.frame[3]) = 0x330000FFFFFF0003ULL; // MUX=0x33 U64_DATA(&msg.frame[4]) = 0x4F07FC0FF0000000ULL; // MUX=0x4F
6.7 BCM 的 CAN FD 支持
当 flags 中设置 CAN_FD_FRAME 时,BCM 消息后附的是 struct canfd_frame 而非 struct can_frame。多路复用掩码仍位于第一个 64 位数据段中。
第 7 章 SocketCAN 核心模块
7.1 核心功能
SocketCAN 核心模块 (can.ko) 实现 PF_CAN 协议族,运行时加载 CAN 协议模块(如 raw、bcm)。它提供 CAN-ID 订阅/取消订阅接口 can_rx_register() / can_rx_unregister()。
7.2 模块参数
/** * @brief can.ko 模块参数 * * - stats_timer: 是否启用每秒一次的核心统计定时器。 * 默认启用,通过 stattimer=0 禁用可减少定时器中断。 * @note debug 参数在 SocketCAN SVN r546 后已移除 */
7.3 procfs 接口
| 文件 | 内容 |
|---|---|
/proc/net/can/rcvlist_all |
未过滤条目列表 |
/proc/net/can/rcvlist_eff |
扩展帧 (EFF) 条目 |
/proc/net/can/rcvlist_err |
错误消息帧掩码 |
/proc/net/can/rcvlist_fil |
掩码/值过滤器 |
/proc/net/can/rcvlist_inv |
反向掩码/值过滤器 |
/proc/net/can/rcvlist_sff |
标准帧 (SFF) 条目 |
/proc/net/can/stats |
核心统计信息 (收发帧数、匹配率等) |
/proc/net/can/reset_stats |
手动重置统计 |
/proc/net/can/version |
核心和 ABI 版本 (Linux 5.10 已移除) |
/** * @brief 检查接收列表的示例 * * 通过查看 /proc/net/can/rcvlist_* 可以确认: * - 过滤规则是否正确注册 * - 匹配计数器是否递增 * - 协议模块标识是否正确 * * @see /proc/net/can/rcvlist_all */
7.4 自定义 CAN 协议模块
实现新协议需要:
/** * @brief 自定义 CAN 协议模块的关键接口 * * - 在 include/linux/can.h 定义新协议编号 * - 包含 include/linux/can/core.h 获取核心接口 * - 使用 can_rx_register() 订阅特定接口的 CAN-ID * - 使用 can_send() 发送 CAN 帧(可选本地回环) * - 注册 CAN 设备通知链以跟踪接口状态变化 * * @see net/can/af_can.c (核心实现) * @see net/can/raw.c (RAW 协议模块) * @see net/can/bcm.c (BCM 协议模块) */
第 8 章 CAN 网络设备驱动
8.1 与字符设备驱动的对比
| 方面 | 字符设备驱动 | 网络设备驱动 |
|---|---|---|
| 实现复杂度 | 高(需自行实现队列、多路分解) | 低(复用内核网络栈) |
| TX 路径 | 管理自己的发送队列 | 从 struct sk_buff 获取帧写入控制器 |
| RX 路径 | 管理自己的接收缓冲区 | 从控制器读取帧分配 sk_buff 向上传递 |
8.2 通用设置
/** * @brief CAN 网络设备初始化要点 * * dev->type = ARPHRD_CAN; // 硬件类型 * dev->flags = IFF_NOARP; // CAN 没有 ARP * dev->mtu = CAN_MTU; // 16 (传统 CAN) * dev->mtu = CANFD_MTU; // 72 (CAN FD 设备) * * @note 每个 sk_buff 的载荷是 struct can_frame 或 struct canfd_frame */
8.3 本地回环支持
/** * @brief 硬件回环的标志设置 * * 若 CAN 控制器在硬件层面支持发送帧的回环: * dev->flags = (IFF_NOARP | IFF_ECHO); * * 设置 IFF_ECHO 后,PF_CAN 核心不再执行软件回环, * 避免帧被重复递送。 */
调试场景:如果发现应用收到重复的 CAN 帧,检查驱动是否正确设置了 IFF_ECHO。若硬件已回环但驱动未设此标志,核心会额外软件回环一次,导致重复。
8.4 控制器硬件过滤器
/** * @brief 硬件过滤器的使用建议 * * 虽然大部分 CAN 控制器支持硬件 ID 过滤,但在多用户 SocketCAN 架构下: * - 硬件过滤器作用于整个接口,影响所有用户 * - PF_CAN 核心提供高效软件过滤,支持每个套接字独立设置 * - 硬件过滤器仅在极深入的嵌入式系统上作为手工优化手段使用 * * @note 作者的 MPC603e @133MHz 运行 4 个 SJA1000 控制器, * 仅使用软件过滤即能承受满总线负载。 */
第 9 章 虚拟 CAN 驱动 (vcan)
虚拟 CAN 接口允许在没有真实 CAN 硬件的情况下收发 CAN 帧,常用于测试和开发。
/** * @brief vcan 接口管理 * * # 创建虚拟 CAN 接口 * $ ip link add type vcan * * # 创建指定名称的接口 * $ ip link add dev vcan42 type vcan * * # 删除接口 * $ ip link del vcan42 * * @note 虚拟接口命名约定为 vcanX (vcan0, vcan1, ...) * 内核模块名为 vcan.ko * 自 2.6.24 起支持 netlink 接口创建 */
第 10 章 CAN FD 驱动支持
10.1 两种 CAN FD 实现
/** * @brief CAN FD 标准的两种实现 * * 1. ISO 兼容: ISO 11898-1:2015 标准 (默认) * 2. 非 ISO: 2012 年白皮书版本 * * CAN FD 控制器分为三类: * - 固定 ISO (大多数现代控制器) * - 固定非 ISO (如 M_CAN IP core v3.0.1) * - 可切换 (如 PEAK PCAN-USB FD) * * @note ISO/非 ISO 模式通过 netlink 通告,ip 工具显示为 FD-NON-ISO */
10.2 CAN FD 配置示例
/** * @brief 配置 CAN FD 设备示例 * * # 设置仲裁段 500kbps,数据段 4Mbps,启用 CAN FD * $ ip link set can0 up type can bitrate 500000 sample-point 0.75 \ * dbitrate 4000000 dsample-point 0.8 fd on * * # 对可切换设备设置非 ISO 模式 * $ ip link set can0 type can fd-non-iso on * * @note DLC 与长度的转换由驱动内部使用 can_fd_dlc2len() 完成, * 用户空间看到的是纯长度值 */
第 11 章 CAN 设备配置接口
11.1 Netlink 配置
CAN 设备通过 netlink 配置,使用 ip 工具(来自 iproute2)。
/**
* @brief CAN 设备配置选项
*
* 基本参数:
* bitrate 位速率 (1..1000000)
* sample-point 采样点 (0.000..0.999)
*
* 硬件无关位时序:
* tq 时间量子 (ns)
* prop-seg 传播段 (1..8)
* phase-seg1 相位缓冲段 1 (1..8)
* phase-seg2 相位缓冲段 2 (1..8)
* sjw 同步跳转宽度 (1..4)
*
* CAN FD 参数 (以 'd' 开头):
* dbitrate, dsample-point, dtq, dprop-seg,
* dphase-seg1, dphase-seg2, dsjw
*
* 控制器模式:
* loopback {on|off} 本地环回
* listen-only {on|off} 仅监听
* triple-sampling {on|off} 三次采样
* one-shot {on|off} 单次发送
* berr-reporting {on|off} 总线错误报告
* fd {on|off} CAN FD 启用
* fd-non-iso {on|off} 非 ISO CAN FD
*/
11.2 启动和停止
/** * @brief CAN 设备的启动与停止 * * # 启动前必须配置位时序,避免使用不安全默认值 * $ ip link set canX up type can bitrate 125000 * * # 总线关闭自动恢复 * $ ip link set canX type can restart-ms 100 * * # 手动重启 * $ ip link set canX type can restart * * @note 总线关闭后设备不再收发。可通过监测 CAN 错误消息帧感知。 */
第 12 章 SocketCAN 资源与致谢
SocketCAN 项目的官方资源和邮件列表在 Linux 源码树 MAINTAINERS 文件中列出,搜索 CAN NETWORK [LAYERS|DRIVERS]。
核心贡献者(部分):
-
Oliver Hartkopp:PF_CAN 核心、过滤器、多种驱动、BCM、SJA1000 驱动
-
Urs Thuermann:PF_CAN 核心、内核集成、套接字接口、RAW、vcan
-
Jan Kizka:RT-SocketCAN 核心、Socket-API 协调
-
Wolfgang Grandegger:RT-SocketCAN 核心和驱动、CAN 设备驱动接口、MSCAN 驱动
-
Marc Kleine-Budde:设计评审、Kernel 2.6 清理、多种驱动
-
Pavel Pisa:位时序计算
-
以及众多驱动贡献者和评审者
SocketCAN 最核心的设计价值在于它正确地选择站在了巨人的肩膀上——没有重新发明轮子去实现专门的 CAN 字符设备子系统,而是将 CAN 的语义巧妙地映射到 BSD 套接字和 Linux 网络栈这个成熟的抽象上。这使得 CAN 编程不再是嵌入式领域孤立的秘术,而是融入了通用网络编程的统一范式。从高效的多路分解、广播管理器到 CAN FD 的无缝扩展,这一架构经受住了二十年的考验。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)