Repository navigation
Refactor/can framework - #12053
Refactor/can framework#12053wdfk-prog wants to merge 20 commits into
Conversation
Simplify the Generic CAN TX path and make hardware mailbox ownership explicit. - remove Generic CAN TX software queues and list-based mailbox allocation - use hardware mailboxes as the only Generic TX queueing resource - make non-blocking TX a one-shot hardware submit with -RT_EBUSY backpressure - move buffering, retry and scheduling policy to protocol/application layers - add TX drain and hardware mailbox abort support - retire TX ownership only on TX_DONE/TX_FAIL terminal events - split TX/RX runtime management from the device adapter - improve RX ISR/callback lifetime synchronization - improve controller reconfiguration and open/close lifecycle handling - update STM32 bxCAN mailbox reuse, terminal and abort semantics
- Track blocking and non-blocking transmissions by hardware mailbox, preserving ownership after timeout until the terminal event. - Prevent STM32 bxCAN non-blocking submissions from reusing mailboxes still owned by blocking senders. - Add RT_CAN_CMD_FORCE_STOP to explicitly stop the controller and retire abandoned TX requests without automatic recovery or retries. - Improve RX interrupt quiescing, TX/error interrupt restoration, and maintenance timer handling during reconfiguration. - Keep forced-stop synchronization under application control. Applications must quiesce TX producers and in-flight writers before invoking FORCE_STOP.
- Expose all three dedicated FDCAN TX buffers as independent Generic CAN mailbox slots. - Add non-blocking TX submission with mailbox selection, hardware busy checks, and software ownership protection. - Report TX completion and cancellation events per mailbox, preserving ownership until a terminal event is received. - Support explicit TX abort and controller stop commands with hardware status and error handling. - Normalize CAN FD DLC encoding across supported HAL representations. - Add typed CAN error events and update Generic CAN error counters without treating controller errors as TX failures. - Capture protocol errors before HAL clears interrupt flags and preserve controller error-state reporting.
- Map three GD32 hardware TX mailboxes to independent Generic CAN slots. - Update non-blocking transmission to select an available mailbox, return its index, and respect blocking TX ownership. - Handle TX_DONE and TX_FAIL independently for each mailbox, preserving ownership until the terminal event is processed. - Add hardware TX abort and controller stop/restart support, with explicit error handling and mailbox status checks. - Preserve ACK-error cancellation for both automatic retransmission modes without prematurely retiring TX ownership or clearing unrelated completion flags. - Correct CAN error register access and error status reporting. - Document CAN0/CAN1 shared filter constraints and update the driver Change Logs.
…ment - Support three hardware TX mailboxes with fixed-mailbox blocking transmission and BSP-selected non-blocking transmission. - Handle TX completion events per mailbox and prevent ACK errors from prematurely releasing TX ownership. - Add controller START/STOP support and preserve shared CAN/USBFS interrupt vectors. - Fix AT32F425 shared IRQ dispatch by checking enabled and pending interrupt sources.
- Support three TX mailboxes and non-blocking transmission with mailbox ownership tracking. - Report TX_DONE and TX_FAIL based on hardware completion status, without releasing mailboxes on intermediate bus errors. - Forward CAN error events to the Generic CAN framework for unified error statistics. - Implement TX abort and controller start/stop operations, and improve configuration error propagation. - Preserve hardware error counters and prevent GET_STATUS from overwriting the last error type.
- Support three hardware TX mailboxes and add non-blocking transmission with mailbox ownership checks. - Report TX_DONE and TX_FAIL from hardware completion flags, while forwarding controller errors without prematurely retiring TX ownership. - Implement controller STOP and TX abort handling with bounded hardware-state confirmation, preserving CAN1/CAN2 shared filters. - Clear stale TX completion flags and hardware RX FIFO entries before controller restart.
- Support three TX mailboxes and nonblocking CAN transmission. - Add TX mailbox abort and controller start/stop operations. - Handle TX completion and failure events per mailbox, and report protocol errors through Generic CAN. - Enable both RX FIFO interrupts and prevent duplicate FIFO releases. - Preserve the default filter configuration and use caller-selected filter banks without modifying input IDs or masks. - Correct extended CAN ID encoding and update the driver for the new Generic CAN operation interfaces.
- Support three hardware TX mailboxes and nonblocking transmission. - Implement per-mailbox TX completion handling and abort/start/stop commands. - Fix RX FIFO release, extended CAN ID encoding, and error event reporting. - Preserve caller-provided CAN filter IDs and masks during configuration.
- Map Generic CAN TX mailboxes to hardware TX FIFO/Queue elements and support non-blocking mailbox selection. - Track per-mailbox TX ownership and report TX_DONE/TX_FAIL from hardware completion and abort events. - Support controller start/stop and mailbox abort commands with bounded hardware TX drain. - Report protocol errors as CAN bus diagnostics without prematurely releasing pending TX ownership. - Return -RT_EBUSY for unavailable TX resources and normalize other hardware errors to -RT_ERROR.
dev_can.c calls rt_can_tx_retire_after_stop() during a forced controller stop, but the prototype was missing from can_internal.h. Every CAN-enabled BSP therefore failed with -Werror=implicit-function-declaration. Add the TX-private declaration next to rt_can_tx_drain_core(); the signature matches the definition in can_tx.c. Help RT-Thread#11779
Register a virtual CAN controller with simulated ops and cover: - device registration and lookup - open/close reference counting and reopen after final close - START(0)/START(1) TX admission and BSP error propagation - blocking mailbox timeout - non-blocking TX ownership retirement via a simulated terminal - RX ISR to software FIFO to rt_device_read() The suite needs no CAN controller, transceiver or bus, so it can run on any BSP and under QEMU. Wire it through can/utest Kconfig and SConscript. Help RT-Thread#11779 Related to RT-Thread#11972
|
👋 感谢您对 RT-Thread 的贡献!Thank you for your contribution to RT-Thread! 为确保代码符合 RT-Thread 的编码规范,请在你的仓库中执行以下步骤运行代码格式化工作流(如果格式化CI运行失败)。 🛠 操作步骤 | Steps
完成后,提交将自动更新至 如有问题欢迎联系我们,再次感谢您的贡献!💐 |
📌 Code Review Assignment🏷️ Tag: bsp_hc32Reviewers: @levizh Changed Files (Click to expand)
🏷️ Tag: bsp_mcxaReviewers: @hywing Changed Files (Click to expand)
🏷️ Tag: bsp_mcxnReviewers: @hywing Changed Files (Click to expand)
🏷️ Tag: bsp_stm32Reviewers: @Liang1795 @hamburger-os @wdfk-prog Changed Files (Click to expand)
🏷️ Tag: componentsReviewers: @Maihuanyi Changed Files (Click to expand)
🏷️ Tag: components_driver_canReviewers: @wdfk-prog Changed Files (Click to expand)
📊 Current Review Status (Last Updated: 2026-11-13 14:44 CST)
📝 Review Instructions
|
|
PR Message
拉取/合并请求描述:(PR description)
关联 Issue
[Discussion][CAN] RT-Thread CAN Framework 重构方案[Feature][CAN] 支持 TX Full Flush 与 Hardware Abort 能力refactor[CAN]: improve CAN driver lifecycle and TX handling为什么提交这份 PR (why to submit this PR)
本 PR 对 RT-Thread Generic CAN Framework 及 STM32 bxCAN 后端进行一次结构性重构。
这次修改并不是针对某一个 CAN BSP 的局部 Bug 修复,而是希望重新明确 Generic CAN Framework 的职责边界:
本次方案来源于 #11767 和 #11759 中暴露出来的一系列问题,但最终实现相比 #11767 最初讨论的
TX Request Pool + Software FIFO + Scheduler方案进一步进行了简化。当前实现不再在 Generic CAN 中维护 TX software queue,而是直接使用 CAN Controller 的 hardware mailbox 作为 Generic TX 的唯一排队资源。
这样可以显著减少 Framework 内部状态、并发关系和资源生命周期,同时让 Nonblocking、Abort、Runtime Reconfiguration 等行为具有更清晰的语义。
1. 旧 CAN TX 设计存在的问题
旧 Generic CAN Framework 中实际上存在两套不同的发送模型。
Blocking TX
Blocking TX 使用:
rt_can_tx_fifort_can_sndbxinx_liststatus.sndchange共同管理 TX mailbox。
但是 CAN Controller 的发送 mailbox 本身就是数量固定、编号固定的硬件资源。
例如 STM32 bxCAN 固定提供:
为这样一个固定、小规模、可以直接索引的资源再建立 linked-list freelist,会增加:
这些结构本身并没有提供额外的发送能力。
因此本 PR 删除 TX mailbox linked-list 管理,将其改为:
一个 software slot 直接对应一个 Framework-visible hardware mailbox。
Nonblocking TX
旧 Nonblocking TX 是另外一套完全不同的路径:
这使 Generic CAN Framework 自己承担了一层 software TX queue。
这样存在几个问题。
1. Blocking / Nonblocking 是两套发送状态机
Blocking TX 维护:
Nonblocking TX 又维护:
实际上两者最终操作的是同一组 CAN hardware mailbox,却由 Framework 维护两套完全不同的资源和状态。
随着 Abort、Runtime Bitrate Change、Bus Recovery、SMP 等能力加入,必须同时考虑两套 TX 状态,维护成本持续增加。
2. Generic CAN 隐式缓存改变了 Nonblocking 的语义
旧实现中:
返回成功并不一定表示:
也可能只是:
因此应用很难准确判断一帧当前究竟处于:
哪一个阶段。
对于需要:
的场景,这种隐式缓存会让发送状态变得模糊。
3. ISR refill 增加了顺序和并发复杂度
旧实现会在
TX_DONE/TX_FAILISR 中继续读取nb_tx_rb并重新向硬件提交。当 refill 失败时,还需要重新把 frame 放回 software queue。
这会进一步引入:
三者之间的并发关系。
Generic CAN 此时不仅是 Device Framework,同时还成为了 TX scheduler。
这超出了本 PR 希望 Generic CAN 承担的职责范围。
2. 为什么不继续实现 Generic TX Request Queue / Scheduler
#11767 最初讨论过一种更完整的统一 TX Engine:
这个方向可以统一 blocking / nonblocking,并建立完整 request ownership。
但是在进一步实现和测试后,本 PR 最终选择了更简单的设计:
原因是发送缓存本质上属于策略,而不是 CAN hardware abstraction 必须提供的机制。
不同协议对于 TX queue 的要求完全不同,例如:
Generic CAN 如果强制提供一个 FIFO,实际上等于替所有上层协议选择了一种固定的缓存策略。
同时对于本身已经有 TX queue 的协议栈,还会形成:
双重缓存。
这会增加:
因此本 PR 将职责重新划分为:
这样 Generic CAN 保持为一个较轻量、确定性的 Hardware/Device Framework。
3. 新的 TX 设计
新的 Generic CAN TX 设计遵循以下原则:
TX_DONE或TX_FAIL;-RT_EBUSY向上层提供明确 backpressure。整体流程:
flowchart TD A["Application / Protocol"] --> B["rt_device_write()"] B --> C{"Blocking?"} C -->|Yes| D["Scan fixed mailbox slots"] D --> E["ops->sendmsg(mailbox)"] E --> F["Hardware mailbox"] C -->|No| G["ops->sendmsg_nonblocking() once"] G -->|RT_EOK| F G -->|-RT_EBUSY| H["Return backpressure to caller"] F --> I["CAN Controller"] I --> J["TX_DONE / TX_FAIL IRQ"] J --> K["rt_hw_can_isr()"] K --> L["Generic TX terminal"] L --> M["pending-- / ownership retire"] M --> N["Mailbox reusable"]4. Blocking TX:删除 linked-list,直接映射 Hardware Mailbox
Blocking TX 不再建立 mailbox freelist。
现在每一个 hardware mailbox 对应一个固定 slot:
每个 slot 只维护:
Blocking send 流程:
这种设计的优势:
5. Nonblocking TX:直接发送,不再由 Generic CAN 缓存
新的 Nonblocking 定义为:
流程:
Generic CAN:
如果用户需要:
由上层自行实现。
这种设计使 Nonblocking 的返回语义非常明确:
6. 删除 Generic Nonblocking TX Ringbuffer
由于 Nonblocking 改为直接 hardware submit,本 PR 删除:
同时删除:
这一整套 ISR refill 逻辑。
因此 TX ISR 现在只负责一个职责:
即:
不再承担 scheduler 的职责。
7. Terminal 是 TX ownership 的唯一结束点
新的 Generic CAN 使用
pending跟踪已经被 hardware 接受、但还没有收到 terminal 的 frame 数量。关键原则是:
只有收到:
或者:
才能结束 hardware ownership。
因此:
Generic CAN 不通过 timeout、abort request 或 close request 直接伪造一个 hardware terminal。
8. 为什么这个设计更容易支持 Abort
#11759 最初提出 Full Flush 时,旧 Framework 需要同时处理:
新的设计已经删除 Generic TX software queue。
因此 Framework 中需要处理的 TX 状态收敛为:
这使 Abort 可以保持非常明确的硬件语义。
RT_CAN_CMD_ABORT_TX重要的是:
Generic CAN 不会在调用
RT_CAN_CMD_ABORT_TX后立即pending--。真正结束 ownership 的仍然是随后到来的
TX_FAIL。这样避免 Abort 和 ISR 并发时出现:
9. Full Flush 的设计也因此被简化
本 PR 不再增加一个负责清除 Generic software queue 的
RT_CAN_CMD_FLUSH_TX,因为 Generic CAN 已经没有 TX software queue。完整 TX silence window 应由不同层分别完成自己的职责。
例如 CANopen LSS Runtime Bitrate Change:
换句话说:
10. TX Drain
新增:
rt_can_tx_drain()用于等待:
Drain 不产生新的发送策略,也不会自动 Abort。
它只回答一个问题:
因此上层可以根据自己的业务要求选择:
Graceful
Forced
11. STM32 bxCAN Hardware Abort
STM32 bxCAN 提供 3 个 TX mailbox。
本 PR 为其实现明确的:
mailbox 映射:
非法 mailbox 返回:
同时 Abort 操作只写目标
ABRQx,避免对 bxCANTSR中其它 W1C terminal flag 进行不必要的 Read-Modify-Write。这是因为
RQCP0/RQCP1/RQCP2属于 Write-1-to-Clear 状态。Abort mailbox 0 时不应该因为寄存器 RMW 意外消费 mailbox 1/2 尚未上报给 Generic CAN 的 terminal。
12. Mailbox 在旧 Terminal 被消费前禁止复用
除了 TME(Transmit Mailbox Empty)以外,本 PR 还需要考虑
RQCPx是否仍然表示前一个 transaction 存在未消费 terminal。因此:
不等价于:
新的 frame 不能在旧
RQCPx仍 pending 时复用相同 mailbox。否则可能出现:
13. Blocking / Nonblocking 共用 Terminal Accounting
虽然 Blocking 和 Nonblocking 的提交接口不同:
但是进入 hardware 后,都统一进入:
Blocking mailbox 如果存在
ACTIVE slot,terminal 会更新该 slot 并唤醒对应 completion。如果 mailbox 没有 blocking owner,则 terminal 用于 retirement 一个已经被 hardware 接受的 Nonblocking TX。
14. TX 并发同步
新的 TX 核心使用独立
tx_lock保护:BSP submit 被定义为 immediate hardware submit,即不能:
因此可以在 TX critical section 中完成:
用于关闭快速 TX IRQ 在 ownership publish 前到来的竞态窗口。
15. Controller Management / Reconfiguration Lifecycle
本次重构不仅处理 TX,同时重新整理 CAN controller management。
旧代码中:
与 TX/RX runtime 的生命周期关系并不清晰。
新的 management transaction 使用:
flowchart TD A["Application stops TX producer"] --> B["can->lock"] B --> C["Close Generic TX admission"] C --> D{"TX completely idle?"} D -->|No| E["Return busy / caller chooses drain or abort"] D -->|Yes| F["Pause RX admission"] F --> G["Wait in-flight RX ISR/callback pins"] G --> H["BSP configure/control"] H --> I["Resume RX"] I --> J["Restore TX admission"]职责划分:
16. RX 为什么仍然保留 Software FIFO
本次只删除 TX software queue,并没有删除 RX FIFO。
这是刻意的设计差异。
RX 的生产者和消费者天然位于不同上下文:
如果 Generic CAN 不提供 RX FIFO,就可能在应用没有立即读取时直接丢失 hardware RX frame。
因此 RX buffering 属于 Device Framework 的合理职责。
而 TX 不同,发送端完全可以通过
-RT_EBUSY把 hardware backpressure 反馈给 caller,由上层决定下一步策略。所以新的原则是:
17. RX ISR / Callback Lifetime
RX 路径增加独立:
RX ISR 处理流程:
这样
recvmsg()和 user callback 不会长时间占用rx_lock。与此同时 close/reconfigure 可以先停止新的 RX admission,再等待
rx_active_isr == 0,之后才释放 RX/HDR runtime。18. 模块职责拆分
原来的
dev_can.c同时承担 device adapter、TX queue、RX queue、ISR processing、lifecycle、control。本 PR 将 Generic CAN 拆分为:
目标是让 TX correctness、RX correctness、Controller lifecycle 三个问题分别具有清晰的 ownership boundary。
19. Public API / BSP Compatibility
本次重构尽量保持现有 RT-Thread CAN 使用接口。
继续保留:
现有 BSP 的中断入口仍然通过
rt_hw_can_isr()向 Generic CAN 上报 RX/TX event。需要特别说明的一项行为变化是 Nonblocking 不再由 Generic CAN 提供 software buffering。
旧行为:
新行为:
如果应用以前依赖
nb_tx_rb进行 burst buffering,需要将该 queue 放到应用或协议层。这不是简单删除能力,而是有意进行的职责边界调整。
20. Kconfig 简化
因为移除了 Generic Nonblocking TX software queue,因此删除与其相关的配置:
不再需要用户考虑 Generic TX FIFO depth、static/dynamic TX buffer 以及 CAN/CAN-FD frame size 对 TX ringbuffer 的影响。
同时:
语义明确为 Generic CAN 可见的 Hardware TX Mailbox 数量。
对于 STM32 bxCAN,实际测试配置为:
对应 controller 的三个 TX mailbox。
21. 与 #11759 Full Flush / Hardware Abort 的关系
#11759 最初的问题本质是:
因此旧方案必须设计一个 Framework Full Flush。
本 PR 删除 Generic TX software queue 后,这个问题被重新拆分:
因此本次方案倾向于提供可组合、语义明确的基础能力:
而不是在 Framework 内部重新建立一个同时处理 software queue、protocol policy 和 hardware transaction 的大型 Flush 状态机。
测试验证
本 PR 已在真实目标板 + 实际 CAN 总线上执行功能和回归测试。
测试总线:
1. Blocking CAN / CANopenNode SDO Block 回归测试 — PASS
使用 CANopenNode 作为实际协议负载,对 blocking CAN TX/RX 路径进行回归。
SDO Block Transfer 测试长度:
同时验证:
最大数据长度稳定性:
2. Communication Reset / Reopen — PASS
验证:
结果:PASS。
3. Nonblocking TX - Paced — PASS
该测试不依赖 CANopen,在干净的 Generic CAN 环境下执行。
结果:PASS。
4. Nonblocking TX - Burst / Backpressure — PASS
连续快速提交 1000 次 Nonblocking TX:
结合 paced test:
满足:
结果:PASS。
5.
RT_CAN_CMD_ABORT_TX参数检查 — PASS6. Hardware TX Abort — PASS
Linux SocketCAN 节点持续发送高优先级:
制造实际 arbitration load。
目标 MCU 提交较低优先级 Nonblocking frame,并立即:
测试结果:
证明:
结果:PASS。
7. Abort 后 Mailbox Recovery — PASS
完成 Hardware Abort 后再次发送 CAN frame。
证明 Abort 后 old ownership 已退休、pending 已恢复、mailbox 可复用、新 TX 可以正常接受和完成。
结果:PASS。
测试总结
-RT_EBUSYBackpressureRT_CAN_CMD_ABORT_TXTX_FAILTerminalNonblocking 和 Abort 专项测试均直接针对 RT-Thread Generic CAN API 执行,不依赖 CANopen。
请提供验证的 bsp 和 config (provide the config and bsp)
BSP:
[填写实际 STM32 BSP 路径]Runtime CAN configuration:
.config:[填写本分支 GitHub Action 编译链接]当前拉取/合并请求的状态 Intent for your PR
必须选择一项 Choose one (Mandatory):
代码质量 Code Quality:
我在这个拉取/合并请求中已经考虑了 As part of this pull request, I've considered the following:
#if 0代码,不包含已经被注释了的代码 All redundant code is removed and cleaned up