Skip to content

refactor[CAN]: improve CAN driver lifecycle and TX handling - #11779

Open
wdfk-prog wants to merge 3 commits into
RT-Thread:masterfrom
wdfk-prog:refactor/can-framework
Open

wdfk-prog wants to merge 3 commits into
RT-Thread:masterfrom
wdfk-prog:refactor/can-framework

Conversation

@wdfk-prog

@wdfk-prog wdfk-prog commented Sep 2, 2026 •

Copy link
Copy Markdown
Contributor

PR Message

拉取/合并请求描述:(PR description)

关联 Issue


为什么提交这份 PR (why to submit this PR)

本 PR 对 RT-Thread Generic CAN Framework 及 STM32 bxCAN 后端进行一次结构性重构。

这次修改并不是针对某一个 CAN BSP 的局部 Bug 修复,而是希望重新明确 Generic CAN Framework 的职责边界:

Generic CAN 负责硬件资源管理、同步、ownership、terminal 和 lifecycle;发送缓存、重试、调度和协议 QoS 策略由上层协议栈或应用自行决定。

本次方案来源于 #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

rt_device_write()
      ↓
_can_int_tx()
      ↓
TX mailbox freelist
      ↓
semaphore
      ↓
sendmsg(mailbox)
      ↓
completion
      ↓
TX_DONE / TX_FAIL

Blocking TX 使用:

  • rt_can_tx_fifo
  • rt_can_sndbxinx_list
  • linked-list freelist
  • semaphore
  • per-mailbox completion
  • status.sndchange

共同管理 TX mailbox。

但是 CAN Controller 的发送 mailbox 本身就是数量固定、编号固定的硬件资源。

例如 STM32 bxCAN 固定提供:

mailbox 0
mailbox 1
mailbox 2

为这样一个固定、小规模、可以直接索引的资源再建立 linked-list freelist,会增加:

  • list insert/remove;
  • semaphore 与 freelist 双重状态;
  • mailbox index 与 software node 之间的映射;
  • terminal 后的软件资源释放流程;
  • Abort 时对“谁拥有这个 mailbox”的判断成本。

这些结构本身并没有提供额外的发送能力。

因此本 PR 删除 TX mailbox linked-list 管理,将其改为:

hardware mailbox N
        ↕
fixed slot[N]

一个 software slot 直接对应一个 Framework-visible hardware mailbox。

Nonblocking TX

旧 Nonblocking TX 是另外一套完全不同的路径:

rt_device_write(nonblocking)
        ↓
sendmsg_nonblocking()
        ↓
hardware available?
   ┌────┴────┐
  Yes        No
   │          │
hardware   nb_tx_rb
mailbox       │
              ↓
        TX_DONE ISR
              ↓
      从 ringbuffer 取帧
              ↓
    sendmsg_nonblocking()
              ↓
      hardware mailbox

这使 Generic CAN Framework 自己承担了一层 software TX queue。

这样存在几个问题。

1. Blocking / Nonblocking 是两套发送状态机

Blocking TX 维护:

freelist
semaphore
completion
sndchange

Nonblocking TX 又维护:

sendmsg_nonblocking
nb_tx_rb
ISR refill

实际上两者最终操作的是同一组 CAN hardware mailbox,却由 Framework 维护两套完全不同的资源和状态。

随着 Abort、Runtime Bitrate Change、Bus Recovery、SMP 等能力加入,必须同时考虑两套 TX 状态,维护成本持续增加。

2. Generic CAN 隐式缓存改变了 Nonblocking 的语义

旧实现中:

rt_device_write(nonblocking)

返回成功并不一定表示:

frame 已经被 CAN controller 接受

也可能只是:

frame 被放入 Generic CAN 的 nb_tx_rb

因此应用很难准确判断一帧当前究竟处于:

Application
    ↓
Generic software queue
    ↓
Hardware mailbox
    ↓
CAN bus

哪一个阶段。

对于需要:

  • Abort;
  • Runtime bitrate switching;
  • Bus recovery;
  • Shutdown;
  • Protocol reset;
  • 严格 TX silence window;

的场景,这种隐式缓存会让发送状态变得模糊。

3. ISR refill 增加了顺序和并发复杂度

旧实现会在 TX_DONE/TX_FAIL ISR 中继续读取 nb_tx_rb 并重新向硬件提交。

当 refill 失败时,还需要重新把 frame 放回 software queue。

这会进一步引入:

Thread producer
       +
ISR consumer/refill
       +
Hardware mailbox

三者之间的并发关系。

Generic CAN 此时不仅是 Device Framework,同时还成为了 TX scheduler。

这超出了本 PR 希望 Generic CAN 承担的职责范围。


2. 为什么不继续实现 Generic TX Request Queue / Scheduler

#11767 最初讨论过一种更完整的统一 TX Engine:

Application
     ↓
TX Request Pool
     ↓
Software FIFO
     ↓
TX Scheduler
     ↓
Mailbox Ownership
     ↓
CAN Hardware

这个方向可以统一 blocking / nonblocking,并建立完整 request ownership。

但是在进一步实现和测试后,本 PR 最终选择了更简单的设计:

Generic CAN 不提供 TX software queue。

原因是发送缓存本质上属于策略,而不是 CAN hardware abstraction 必须提供的机制。

不同协议对于 TX queue 的要求完全不同,例如:

CANopen
    -> 自己已经具有 TX buffer / inhibit / priority 等机制

Motor control
    -> 可能只需要保留最新 command

Diagnostic protocol
    -> 可能需要 FIFO

Realtime control
    -> 可能宁愿直接丢弃旧帧,也不能排队增加 latency

Gateway
    -> 可能需要 priority queue / rate limit

Generic CAN 如果强制提供一个 FIFO,实际上等于替所有上层协议选择了一种固定的缓存策略。

同时对于本身已经有 TX queue 的协议栈,还会形成:

Protocol TX queue
        ↓
Generic CAN TX queue
        ↓
Hardware mailbox

双重缓存。

这会增加:

  • RAM 消耗;
  • latency;
  • queue depth 不确定性;
  • flush 范围;
  • abort 范围;
  • reset/reconfigure 的复杂度。

因此本 PR 将职责重新划分为:

┌─────────────────────────────────┐
│ Protocol / Application          │
│                                 │
│ Optional queue                  │
│ Retry policy                    │
│ Priority / QoS                  │
│ Drop / overwrite policy         │
└───────────────┬─────────────────┘
                │
                ▼
┌─────────────────────────────────┐
│ Generic CAN                     │
│                                 │
│ Hardware submit                 │
│ Mailbox ownership               │
│ Blocking wait                   │
│ TX terminal accounting          │
│ Drain / Abort                   │
│ RX lifetime                     │
│ Controller lifecycle            │
└───────────────┬─────────────────┘
                │
                ▼
┌─────────────────────────────────┐
│ BSP / CAN Controller            │
│                                 │
│ Hardware mailbox                │
│ IRQ                             │
│ Abort                           │
└─────────────────────────────────┘

这样 Generic CAN 保持为一个较轻量、确定性的 Hardware/Device Framework。


3. 新的 TX 设计

新的 Generic CAN TX 设计遵循以下原则:

  1. Generic CAN 不缓存待发送 CAN frame;
  2. Hardware mailbox 是 Generic TX 唯一的排队资源;
  3. BSP submit 必须是短路径硬件操作,不等待 CAN 总线发送完成;
  4. Blocking 与 Nonblocking 共享同一套 hardware terminal accounting;
  5. Hardware accepted frame 必须最终对应一个 TX_DONE 或 TX_FAIL;
  6. Hardware mailbox 在旧 terminal 被 Framework 消费之前不能被复用;
  7. Abort request 本身不释放 ownership;
  8. ownership 只由最终 hardware terminal 结束;
  9. 上层如果需要 software queue,应自行实现;
  10. Framework 通过 -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"]
Loading

4. Blocking TX:删除 linked-list,直接映射 Hardware Mailbox

Blocking TX 不再建立 mailbox freelist。

现在每一个 hardware mailbox 对应一个固定 slot:

slot[0] <-> hardware mailbox 0
slot[1] <-> hardware mailbox 1
slot[2] <-> hardware mailbox 2

每个 slot 只维护:

state
completion

Blocking send 流程:

rt_device_write()
        ↓
scan FREE slot
        ↓
ops->sendmsg(can, msg, mailbox)
        ↓
hardware accepts
        ↓
slot = ACTIVE
pending++
        ↓
wait slot completion
        ↓
TX_DONE / TX_FAIL
        ↓
slot = DONE / FAILED
pending--
        ↓
wake blocking caller
        ↓
slot = FREE

这种设计的优势:

  • hardware mailbox 与 software ownership 一一对应;
  • 不再需要 TX freelist;
  • 不再需要 list node 分配/释放;
  • Abort 可以直接使用 mailbox index;
  • Terminal 可以直接定位 blocking owner;
  • mailbox 数量很小时固定数组比 linked-list 更直接;
  • 状态更容易 Review 和 Debug。

5. Nonblocking TX:直接发送,不再由 Generic CAN 缓存

新的 Nonblocking 定义为:

尝试一次立即 hardware submit。

流程:

rt_device_write(nonblocking = 1)
        ↓
ops->sendmsg_nonblocking()
        ↓
scan hardware mailbox once
        ↓
┌───────────────────────┐
│ 有可用 hardware slot │
└───────────┬───────────┘
            ↓
          RT_EOK
            ↓
        pending++
            ↓
         return

如果无可用 mailbox:

        -RT_EBUSY
            ↓
       立即返回 caller

Generic CAN:

不会 retry
不会 sleep
不会进入 software FIFO
不会在 TX ISR 中 refill software queue

如果用户需要:

retry
queue
priority
drop
latest-value overwrite
rate limit

由上层自行实现。

这种设计使 Nonblocking 的返回语义非常明确:

RT_EOK
    = 已经被 hardware TX resource 接受

-RT_EBUSY
    = 当前 hardware TX resource 不可用

6. 删除 Generic Nonblocking TX Ringbuffer

由于 Nonblocking 改为直接 hardware submit,本 PR 删除:

nb_tx_rb
nb_tx_rb_pool
RT_CAN_NB_TX_FIFO_SIZE
RT_CAN_MALLOC_NB_TX_BUFFER

同时删除:

TX_DONE ISR
    ↓
读取 nb_tx_rb
    ↓
重新 submit
    ↓
失败后重新塞回 ringbuffer

这一整套 ISR refill 逻辑。

因此 TX ISR 现在只负责一个职责:

处理 Hardware TX Terminal

即:

TX_DONE
或
TX_FAIL

不再承担 scheduler 的职责。


7. Terminal 是 TX ownership 的唯一结束点

新的 Generic CAN 使用 pending 跟踪已经被 hardware 接受、但还没有收到 terminal 的 frame 数量。

关键原则是:

Submit success != transaction finished

只有收到:

RT_CAN_EVENT_TX_DONE

或者:

RT_CAN_EVENT_TX_FAIL

才能结束 hardware ownership。

因此:

hardware submit
        ↓
pending++
        ↓
...
        ↓
TX_DONE / TX_FAIL
        ↓
pending--

Generic CAN 不通过 timeout、abort request 或 close request 直接伪造一个 hardware terminal。


8. 为什么这个设计更容易支持 Abort

#11759 最初提出 Full Flush 时,旧 Framework 需要同时处理:

1. Generic software TX queue
2. Hardware mailbox pending frame
3. Blocking sender

新的设计已经删除 Generic TX software queue。

因此 Framework 中需要处理的 TX 状态收敛为:

Hardware accepted frame
        +
Blocking waiter(如果存在)

这使 Abort 可以保持非常明确的硬件语义。

RT_CAN_CMD_ABORT_TX

RT_CAN_CMD_ABORT_TX(mailbox)
        ↓
BSP requests hardware abort
        ↓
CAN controller terminates mailbox
        ↓
TX_FAIL IRQ
        ↓
rt_hw_can_isr()
        ↓
Generic terminal handler
        ↓
pending--
        ↓
blocking waiter receives failure
        ↓
mailbox becomes reusable

重要的是:

ABORT_TX 请求成功不等于 TX ownership 已结束。

Generic CAN 不会在调用 RT_CAN_CMD_ABORT_TX 后立即 pending--。

真正结束 ownership 的仍然是随后到来的 TX_FAIL。

这样避免 Abort 和 ISR 并发时出现:

double release
double pending--
completion mismatch
mailbox reused before old terminal

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:

Disable CANopen TX producer
        ↓
clear/stop CANopen's own TX queue if required
        ↓
rt_can_tx_drain()
        │
        ├─ graceful completion
        │
        └─ timeout / policy decision
              ↓
       RT_CAN_CMD_ABORT_TX
       / RT_CAN_CMD_ABORT_ALL
              ↓
       wait TX terminal / drain
              ↓
RT_CAN_CMD_SET_BAUD
              ↓
PRE/POST timing window
              ↓
reconfigure CAN filters if required
              ↓
Enable CANopen TX

换句话说:

Protocol queue
    -> Protocol owns it

Generic hardware ownership
    -> Generic CAN owns it

Hardware abort
    -> BSP owns it

10. TX Drain

新增:

rt_can_tx_drain()

用于等待:

pending == 0
+
all blocking slots == FREE

Drain 不产生新的发送策略,也不会自动 Abort。

它只回答一个问题:

当前 Generic CAN 是否已经没有 hardware-owned TX。

因此上层可以根据自己的业务要求选择:

Graceful

stop producer
    ↓
drain
    ↓
reconfigure

Forced

stop producer
    ↓
abort hardware TX
    ↓
wait TX_FAIL terminal
    ↓
drain
    ↓
reconfigure

11. STM32 bxCAN Hardware Abort

STM32 bxCAN 提供 3 个 TX mailbox。

本 PR 为其实现明确的:

RT_CAN_CMD_ABORT_TX

mailbox 映射:

0 -> ABRQ0
1 -> ABRQ1
2 -> ABRQ2

非法 mailbox 返回:

-RT_EINVAL

同时 Abort 操作只写目标 ABRQx,避免对 bxCAN TSR 中其它 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。

因此:

Hardware 已经发送完成

不等价于:

Generic CAN 已经处理完该 mailbox 的 terminal

新的 frame 不能在旧 RQCPx 仍 pending 时复用相同 mailbox。

否则可能出现:

Frame A completed
        ↓
A terminal 尚未进入 Generic CAN
        ↓
mailbox 被 Frame B 复用
        ↓
A 的 late terminal
        ↓
错误结束 Frame B ownership

13. Blocking / Nonblocking 共用 Terminal Accounting

虽然 Blocking 和 Nonblocking 的提交接口不同:

Blocking
    -> sendmsg(mailbox)

Nonblocking
    -> sendmsg_nonblocking()

但是进入 hardware 后,都统一进入:

pending accounting
        ↓
TX_DONE / TX_FAIL
        ↓
rt_can_tx_isr_core()

Blocking mailbox 如果存在 ACTIVE slot,terminal 会更新该 slot 并唤醒对应 completion。

如果 mailbox 没有 blocking owner,则 terminal 用于 retirement 一个已经被 hardware 接受的 Nonblocking TX。


14. TX 并发同步

新的 TX 核心使用独立 tx_lock 保护:

TX runtime publication
accepting
blocking slot state
pending
hardware submit publication
terminal accounting

BSP submit 被定义为 immediate hardware submit,即不能:

sleep
等待 TX complete
take blocking semaphore/mutex

因此可以在 TX critical section 中完成:

hardware submit
+
Generic ownership publication

用于关闭快速 TX IRQ 在 ownership publish 前到来的竞态窗口。


15. Controller Management / Reconfiguration Lifecycle

本次重构不仅处理 TX,同时重新整理 CAN controller management。

旧代码中:

open
close
SET_BAUD
SET_MODE
SET_FILTER
START/STOP

与 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"]
Loading

职责划分:

can->lock
    -> thread-side management serialization

tx_lock
    -> TX ownership / terminal / admission

rx_lock
    -> RX FIFO / HDR / RX lifetime

16. RX 为什么仍然保留 Software FIFO

本次只删除 TX software queue,并没有删除 RX FIFO。

这是刻意的设计差异。

RX 的生产者和消费者天然位于不同上下文:

CAN RX IRQ
    -> producer

Application rt_device_read()
    -> consumer

如果 Generic CAN 不提供 RX FIFO,就可能在应用没有立即读取时直接丢失 hardware RX frame。

因此 RX buffering 属于 Device Framework 的合理职责。

而 TX 不同,发送端完全可以通过 -RT_EBUSY 把 hardware backpressure 反馈给 caller,由上层决定下一步策略。

所以新的原则是:

RX:
    Generic CAN retains software FIFO

TX:
    Generic CAN does not provide software FIFO

17. RX ISR / Callback Lifetime

RX 路径增加独立:

rx_lock
rx_active_isr

RX ISR 处理流程:

IRQ
 ↓
under rx_lock:
    acquire RX lifetime pin
 ↓
outside rx_lock:
    BSP recvmsg()
 ↓
under rx_lock:
    publish RX FIFO/HDR state
    snapshot callback
 ↓
outside rx_lock:
    execute callback
 ↓
under rx_lock:
    release lifetime pin

这样 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 拆分为:

dev_can.c
    -> RT-Thread device adapter
    -> open / close
    -> read / write routing
    -> control / management
    -> ISR dispatch

can_tx.c
    -> TX slots
    -> blocking wait
    -> nonblocking submit
    -> pending accounting
    -> terminal
    -> drain / abort

can_rx.c
    -> RX FIFO
    -> HDR routing
    -> RX ISR
    -> callback lifetime
    -> pause / resume

can_internal.h
    -> Generic CAN private contracts

目标是让 TX correctness、RX correctness、Controller lifecycle 三个问题分别具有清晰的 ownership boundary。


19. Public API / BSP Compatibility

本次重构尽量保持现有 RT-Thread CAN 使用接口。

继续保留:

rt_device_open()
rt_device_read()
rt_device_write()
rt_device_control()

struct rt_can_msg

rt_hw_can_isr(can, event)

现有 BSP 的中断入口仍然通过 rt_hw_can_isr() 向 Generic CAN 上报 RX/TX event。

需要特别说明的一项行为变化是 Nonblocking 不再由 Generic CAN 提供 software buffering。

旧行为:

hardware busy
    ↓
Generic queue
    ↓
write returns accepted

新行为:

hardware busy
    ↓
-RT_EBUSY

如果应用以前依赖 nb_tx_rb 进行 burst buffering,需要将该 queue 放到应用或协议层。

这不是简单删除能力,而是有意进行的职责边界调整。


20. Kconfig 简化

因为移除了 Generic Nonblocking TX software queue,因此删除与其相关的配置:

RT_CAN_NB_TX_FIFO_SIZE
RT_CAN_MALLOC_NB_TX_BUFFER

不再需要用户考虑 Generic TX FIFO depth、static/dynamic TX buffer 以及 CAN/CAN-FD frame size 对 TX ringbuffer 的影响。

同时:

RT_CANSND_BOX_NUM

语义明确为 Generic CAN 可见的 Hardware TX Mailbox 数量。

对于 STM32 bxCAN,实际测试配置为:

sndboxnumber = 3

对应 controller 的三个 TX mailbox。


21. 与 #11759 Full Flush / Hardware Abort 的关系

#11759 最初的问题本质是:

Upper-layer TX disabled
        ↓
但 Generic software queue 仍有 frame
        ↓
Hardware mailbox 仍有 frame
        ↓
Blocking sender 仍可能等待

因此旧方案必须设计一个 Framework Full Flush。

本 PR 删除 Generic TX software queue 后,这个问题被重新拆分:

Upper-layer buffered frame
    -> upper layer owns cancellation

Generic accepted hardware TX
    -> drain / terminal accounting

Hardware pending TX
    -> RT_CAN_CMD_ABORT_TX

Controller reconfiguration
    -> Generic management lifecycle

因此本次方案倾向于提供可组合、语义明确的基础能力:

TX producer gate
+
rt_can_tx_drain()
+
RT_CAN_CMD_ABORT_TX
+
controller management

而不是在 Framework 内部重新建立一个同时处理 software queue、protocol policy 和 hardware transaction 的大型 Flush 状态机。


测试验证

本 PR 已在真实目标板 + 实际 CAN 总线上执行功能和回归测试。

测试总线:

CAN bitrate: 1 Mbit/s

MCU:
    RT-Thread Generic CAN
    STM32 bxCAN
    sndboxnumber = 3

Peer:
    Linux SocketCAN
    can-utils

1. Blocking CAN / CANopenNode SDO Block 回归测试 — PASS

使用 CANopenNode 作为实际协议负载,对 blocking CAN TX/RX 路径进行回归。

SDO Block Transfer 测试长度:

32 Bytes       PASS
900 Bytes      PASS
1024 Bytes     PASS
1025 Bytes     PASS
2048 Bytes     PASS

同时验证:

Read-only object write abort + recovery        PASS
Stopped-node timeout recovery                  PASS
Client abort + recovery                        PASS
Block -> Expedited regression                  PASS
Block -> Segmented regression                  PASS

最大数据长度稳定性:

2048 Bytes × 100 round trips = PASS

2. Communication Reset / Reopen — PASS

验证:

CAN close
    ↓
controller deinit
    ↓
CAN reopen
    ↓
RX/TX runtime rebuild
    ↓
filter reconfiguration
    ↓
communication recovery

结果:PASS。

3. Nonblocking TX - Paced — PASS

该测试不依赖 CANopen,在干净的 Generic CAN 环境下执行。

accepted = 100
busy     = 0
TX_FAIL  = 0

结果:PASS。

4. Nonblocking TX - Burst / Backpressure — PASS

连续快速提交 1000 次 Nonblocking TX:

accepted  = 49
-RT_EBUSY = 951
other_err = 0

结合 paced test:

accepted_total = 149
TX_DONE        = 149
TX_FAIL        = 0

满足:

accepted_total == TX_DONE + TX_FAIL
149            == 149

结果:PASS。

5. RT_CAN_CMD_ABORT_TX 参数检查 — PASS

invalid-mailbox boundary PASS

6. Hardware TX Abort — PASS

Linux SocketCAN 节点持续发送高优先级:

CAN ID = 0x001

制造实际 arbitration load。

目标 MCU 提交较低优先级 Nonblocking frame,并立即:

RT_CAN_CMD_ABORT_TX(mailbox 0)

测试结果:

caught hardware abort at attempt=0
TX_FAIL=1

证明:

frame hardware pending
        ↓
ABRQ0
        ↓
hardware abort
        ↓
TX_FAIL terminal
        ↓
Generic pending retirement

结果:PASS。

7. Abort 后 Mailbox Recovery — PASS

完成 Hardware Abort 后再次发送 CAN frame。

recovery send PASS

证明 Abort 后 old ownership 已退休、pending 已恢复、mailbox 可复用、新 TX 可以正常接受和完成。

结果:PASS。

测试总结

测试项目 结果
Blocking CAN TX/RX PASS
SDO Block 32 / 900 / 1024 / 1025 / 2048 Bytes PASS
SDO Block 2048 Bytes × 100 PASS
SDO Abort / Timeout Recovery PASS
Block -> Expedited / Segmented Regression PASS
Communication Reset / CAN Reopen PASS
Nonblocking Paced TX 100 次 PASS
Nonblocking Burst / -RT_EBUSY Backpressure PASS
Nonblocking Terminal Accounting PASS
Invalid Abort Mailbox PASS
Hardware RT_CAN_CMD_ABORT_TX PASS
Abort TX_FAIL Terminal PASS
Abort 后 Mailbox Recovery PASS

Nonblocking 和 Abort 专项测试均直接针对 RT-Thread Generic CAN API 执行,不依赖 CANopen。


请提供验证的 bsp 和 config (provide the config and bsp)

  • BSP: [填写实际 STM32 BSP 路径]

  • Runtime CAN configuration:

bitrate       = 1000000
sndboxnumber  = 3
  • .config:
CONFIG_RT_USING_CAN=y
[补充实际 BSP CAN / CAN2 / pin / clock 等配置]
  • action:

[填写本分支 GitHub Action 编译链接]

当前拉取/合并请求的状态 Intent for your PR

必须选择一项 Choose one (Mandatory):

  • 本拉取/合并请求是一个草稿版本 This PR is for a code-review and is intended to get feedback
  • 本拉取/合并请求是一个成熟版本 This PR is mature, and ready to be integrated into the repo

代码质量 Code Quality:

我在这个拉取/合并请求中已经考虑了 As part of this pull request, I've considered the following:

  • 已经仔细查看过代码改动的对比 Already check the difference between PR and old code
  • 代码风格正确,包括缩进空格,命名及其他风格 Style guide is adhered to, including spacing, naming and other styles
  • 没有垃圾代码,代码尽量精简,不包含#if 0代码,不包含已经被注释了的代码 All redundant code is removed and cleaned up
  • 所有变更均有原因及合理的,并且不会影响到其他软件组件代码或BSP All modifications are justified and not affect other components or BSP
  • 对难懂代码均提供对应的注释 I've commented appropriately where code is tricky
  • 代码是高质量的 Code in this PR is of high quality
  • 已经使用formatting 等源码格式化工具确保格式符合RT-Thread代码规范 This PR complies with RT-Thread code specification
  • 如果是新增bsp, 已经添加ci检查到.github/ALL_BSP_COMPILE.json 详细请参考链接BSP自查

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown

👋 感谢您对 RT-Thread 的贡献!Thank you for your contribution to RT-Thread!

为确保代码符合 RT-Thread 的编码规范,请在你的仓库中执行以下步骤运行代码格式化工作流(如果格式化CI运行失败)。
To ensure your code complies with RT-Thread's coding style, please run the code formatting workflow by following the steps below (If the formatting of CI fails to run).


🛠 操作步骤 | Steps

  1. 前往 Actions 页面 | Go to the Actions page
    点击进入工作流 → | Click to open workflow →

  2. 点击 Run workflow | Click Run workflow

  • Use workflow from 保持默认分支(通常为 master)
    Keep the default branch (usually master) in Use workflow from
  • 在 branch 输入框填写 PR 分支 refactor/can-framework
    Enter PR branch refactor/can-framework in the branch field
  • 设置需排除的文件/目录(目录请以"/"结尾)
    Set files/directories to exclude (directories should end with "/")
  1. 等待工作流完成 | Wait for the workflow to complete
    格式化后的代码将作为独立提交推送至你的分支。
    The formatting changes will be pushed to your branch as a separate commit.

完成后,提交将自动更新至 refactor/can-framework 分支,关联的 Pull Request 也会同步更新。
Once completed, commits will be pushed to the refactor/can-framework branch automatically, and the related Pull Request will be updated.

如有问题欢迎联系我们,再次感谢您的贡献!💐
If you have any questions, feel free to reach out. Thanks again for your contribution!

@github-actions

github-actions Bot commented Sep 2, 2026 •

Copy link
Copy Markdown

📌 Code Review Assignment

🏷️ Tag: bsp_stm32

Reviewers: @Liang1795 @hamburger-os @wdfk-prog

Changed Files (Click to expand)
  • bsp/stm32/libraries/HAL_Drivers/drivers/drv_can.c

🏷️ Tag: components

Reviewers: @Maihuanyi

Changed Files (Click to expand)
  • components/drivers/can/Kconfig
  • components/drivers/can/SConscript
  • components/drivers/can/can_internal.h
  • components/drivers/can/can_rx.c
  • components/drivers/can/can_tx.c
  • components/drivers/can/dev_can.c
  • components/drivers/include/drivers/dev_can.h

🏷️ Tag: components_driver_can

Reviewers: @wdfk-prog

Changed Files (Click to expand)
  • components/drivers/can/Kconfig
  • components/drivers/can/SConscript
  • components/drivers/can/can_internal.h
  • components/drivers/can/can_rx.c
  • components/drivers/can/can_tx.c
  • components/drivers/can/dev_can.c

📊 Current Review Status (Last Updated: 2026-11-07 19:15 CST)


📝 Review Instructions

  1. 维护者可以通过单击此处来刷新审查状态: 🔄 刷新状态
    Maintainers can refresh the review status by clicking here: 🔄 Refresh Status

  2. 确认审核通过后评论 LGTM/lgtm
    Comment LGTM/lgtm after confirming approval

  3. PR合并前需至少一位维护者确认
    PR must be confirmed by at least one maintainer before merging

ℹ️ 刷新CI状态操作需要具备仓库写入权限。
ℹ️ Refresh CI status operation requires repository Write permission.

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
@wdfk-prog
wdfk-prog force-pushed the refactor/can-framework branch from 44833a0 to edb6552 Compare September 2, 2026 05:30
@github-actions

github-actions Bot commented Sep 2, 2026 •

Copy link
Copy Markdown

MemBrowse Memory Report · Explore what changed →

at32f415-start

  • RAM: .bss -8 B (-0.2%, 4,572 B / 32,768 B, total: 14% used)
  • ROM: .text +44 B (+0.1%, 65,444 B / 262,144 B, total: 25% used)

esp32-c3

  • dram0_0_seg: .dram0.bss +9,208 B, .dram0.data +168 B, .dram0.dummy -21,504 B (-5.5%, 208,608 B / 325,120 B, total: 64% used)
  • drom0_0_seg: .eh_frame +78,544 B, .eh_frame_hdr +12,376 B, .flash.rodata +27,368 B, .flash_rodata_dummy +196,608 B (+76.6%, 725,908 B / 8,388,576 B, total: 9% used)
  • iram0_0_seg: .iram0.text -21,100 B, .iram0.text_end -404 B (-28.8%, 53,248 B / 325,120 B, total: 16% used)
  • iram0_2_seg: .flash.text +199,784 B, .text +474 B (+87.2%, 429,852 B / 8,388,576 B, total: 5% used)
  • rtc_iram_seg: .rtc.text +8 B (+60.0%, 64 B / 8,192 B, total: 1% used)

gd32105r-start

  • CODE: .text +40 B (+0.1%, 69,356 B / 262,144 B, total: 26% used)
  • DATA: .bss -4 B (-0.1%, 3,748 B / 98,304 B, total: 4% used)

hc32f334

  • FLASH: .text +84 B (+0.1%, 99,612 B / 131,072 B, total: 76% used)
  • RAM: .bss +80 B (+1.5%, 5,328 B / 32,768 B, total: 16% used)

hpmicro-hpm5301evklite

  • DLM: .bss -2,320 B, .data -464 B (+0.5%, 58,560 B / 131,072 B, total: 45% used)
  • ILM: .fast -14,400 B, .vectors -96 B (-37.9%, 23,712 B / 131,072 B, total: 18% used)
  • XPI0: .data -464 B, .fast -14,400 B, .text -33,264 B, .vectors -96 B (-38.6%, 76,748 B / 1,048,576 B, total: 7% used)

infineon-psoc6

  • flash: .text +40 B (+0.0%, 116,212 B / 262,144 B, total: 44% used)

k230

  • SRAM: .bss +62,528 B, .data +1,128 B, .eh_frame +36,296 B, .sbss +176 B, .srodata.cst8 +40 B, .stack +24 B, .text +166,664 B (+23.6%, 1,395,268 B / 60,682,240 B, total: 2% used)

loongson-ls1cdev

  • Code: .text +4,888 B (+1.3%, 368,192 B)
  • Data: .bss -4 B (-0.0%, 59,808 B)

nordic-nrf51822

  • FLASH: .text +48 B (+0.1%, 52,056 B / 262,144 B, total: 20% used)

nuvoton-m487

  • CODE: .text +556 B (+0.2%, 356,580 B / 524,288 B, total: 68% used)
  • DATA: .bss -4 B (-0.0%, 101,928 B / 163,840 B, total: 62% used)

qemu-virt64-aarch64

  • Code: .eh_frame -14,952 B, .text -52,544 B (-6.3%, 1,009,628 B)
  • Data: .bss +598,144 B, .data -47,560 B (+106.4%, 1,068,104 B)

raspberry-pico-rp2040

  • FLASH: .text +56 B (+0.0%, 114,192 B / 2,097,152 B, total: 5% used)

renesas-ra2l1

  • FLASH: .text +48 B (+0.0%, 98,304 B / 262,144 B, total: 38% used)

simulator

  • Code: .eh_frame +4 B, .rela.dyn -552 B, .rodata +9 B, .text +172 B (-0.0%, 2,673,058 B)
  • Data: .data -704 B (-0.0%, 1,609,028 B)

stm32f407-rt-spark

  • CODE: .text +292 B (+0.3%, 86,088 B / 1,048,576 B, total: 8% used)

stm32l475-atk-pandora-llvm

  • RAM1: .stack +4 B (+0.1%, 4,244 B / 98,304 B, total: 4% used)
  • ROM: .text +276 B (-0.9%, 82,712 B / 524,288 B, total: 16% used)

wch-ch32v208w-r0

  • FLASH: .text -64 B (-0.0%, 159,888 B / 491,520 B, total: 33% used)

x86

  • Code: .text +64 B (+0.0%, 198,941 B)
  • Data: .bss -32 B (-0.2%, 14,548 B)

xuantie-e901plus

  • DSRAM: .bss -8 B (-0.1%, 14,004 B / 524,288 B, total: 3% used)
  • ISRAM: .rodata +16 B, .text +48 B (+0.1%, 46,424 B / 131,072 B, total: 35% used)

No memory changes detected for:


Catch memory regressions in CI with MemBrowse. Free for open source.

@wdfk-prog

Copy link
Copy Markdown
Contributor Author

@BernardXiong @meng-plus @Rbb666 @Guozhanxin @illustriousness @gbcwbz @CYFS3

@CYFS3

CYFS3 commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

其他bsp是不是也得从新适配can?

@wdfk-prog

wdfk-prog commented Sep 3, 2026 •

Copy link
Copy Markdown
Contributor Author

其他bsp是不是也得从新适配can?

  • 不需要,可以用其他BSP验证一下,我手上并没有无法验证
  • STM32的更新是为了支持RT_CAN_CMD_ABORT_TX功能,其他改动是AI觉得可以优化的😄, 看着改动很多是进行了CI格式化
  • 特定设计做了兼容的,否则这个就应该叫CAN_V2了

@illustriousness illustriousness left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

结论 / Conclusion: Request changes

当前实现改变了 Generic CAN 与所有 BSP 之间的运行时契约,但只迁移了 STM32 bxCAN,因此暂不满足“其他 BSP 无需适配”的兼容性要求。

🔴 [Concurrency/并发]: Do not call un-migrated BSP send callbacks under irqsave / 不要在关中断锁内调用尚未迁移的 BSP 回调

English: ops->sendmsg() is now called while tx_lock holds IRQs disabled, but existing BSP callbacks have not been migrated to the immediate-submit contract. HPMicro calls rt_thread_mdelay(1), NS800 waits for its TX ISR to change mbState, and HT32 busy-waits for the mailbox. These paths can sleep with interrupts disabled or deadlock because the terminal ISR cannot run. Please migrate every in-tree CAN BSP first, or redesign submission with a reserving state so the BSP callback can execute outside the IRQ-disabled section.

中文:当前在 rt_spin_lock_irqsave() 保护区内调用 BSP sendmsg(),但现有 BSP 并不都满足“立即返回、不可等待”的新契约。HPMicro 调用 rt_thread_mdelay(),NS800 等待 TX ISR 更新状态,HT32 忙等邮箱。这会造成关中断睡眠,或因 TX ISR 无法执行而死锁。必须同步迁移所有后端,或增加 RESERVING 状态,让硬件提交在锁外执行。

🟠 [Lifecycle/生命周期]: Final close now requires every BSP to implement START(0) / 最终关闭强制要求所有 BSP 实现 START(0)

English: Final close now treats RT_CAN_CMD_START(0) as mandatory and returns before Generic runtime cleanup when the BSP rejects it. Many existing backends do not implement this command; Nuvoton returns -RT_EINVAL, while Renesas and HT32 return -RT_ERROR. At this point the device core has already reduced ref_count to zero, leaving RX/TX runtime allocated and admission closed, so reopen produces a partially initialized, unusable device. Please retain the legacy optional behavior or migrate/capability-gate all BSPs and test open-close-open.

中文:rt_device_close() 进入驱动回调前已经把 ref_count 减为 0;这里若 BSP 不支持 RT_CAN_CMD_START(0),会在释放 RX/TX runtime 之前返回。现有 Nuvoton、Renesas、HT32 等后端会拒绝该命令,导致设备处于 ref_count=0、runtime 未释放、TX admission 已关闭的残缺状态,后续 reopen 也无法恢复。需要保留旧的可选行为,或为所有 BSP 完成能力适配和 open-close-open 回归。

🔴 [Ownership/所有权]: Do not release ownership before a hardware terminal / 不要在硬件 terminal 前释放 ownership

English: On timeout the blocking path decrements pending and marks the slot FREE without aborting the hardware request. Management can therefore observe TX idle and reconfigure while the old frame is still hardware-owned. Its late terminal may also be treated as a nonblocking terminal and decrement an unrelated pending frame. Keep a TIMED_OUT or ABANDONED slot until TX_DONE/TX_FAIL retires it, or abort and wait for the terminal before releasing ownership.

中文:发送超时只代表等待超时,并不代表硬件已经结束请求。当前代码却执行 pending-- 并将 slot 设为 FREE。随后 drain/reconfigure 会错误判断 TX 已空闲;旧帧仍可能发送,迟到的 terminal 还可能冲掉其他 nonblocking 请求的 pending。应保留 TIMED_OUT/ABANDONED ownership,直到真实 TX_DONE/TX_FAIL 到来。

🟠 [Compatibility/兼容性]: Existing BSPs lose status polling / 现有 BSP 默认失去状态轮询

English: Although RT_CAN_USING_STATUS_POLLING defaults to y, all 30 checked-in CAN-enabled .config and rtconfig.h files lack this new symbol. Normal SCons builds consume the checked-in rtconfig.h, so status polling is disabled and RT_CAN_CMD_SET_STATUS_IND returns -RT_ENOSYS on every existing CAN BSP. Please preserve the previous source-level default or regenerate all affected BSP configs through the supported configuration flow.

中文:仓库中 30 个启用 CAN 的 .config/rtconfig.h 都没有新宏。普通 SCons 构建不会重新计算 Kconfig 的 default y,因此现有 BSP 均不会编译状态轮询路径,并会在 RT_CAN_CMD_SET_STATUS_IND 返回 -RT_ENOSYS。需要保留原有默认行为,或通过配置工具迁移全部相关 BSP。

补充 / Additional:

  • STM32 bxCAN 将 AutoBusOff 从启用改为禁用、AutoRetransmission 从禁用改为启用,但没有增加明确的 bus-off 恢复路径;该线上行为变化需要恢复、配置化,或单独说明并验证。
  • PR 标题和 commit 不符合 [module][subsystem] Description 格式;建议改为 [components][can] Refactor TX ownership and controller lifecycle,并 squash 格式化 commit。
  • PR 描述中的 BSP、.config 和 Action 链接仍是占位符。
  • git diff --check、ClangFormat 检查通过;FT2004 的 Generic CAN 与 BSP CAN 对象可编译。GitHub 当前只有 CLA 成功,没有构建检查。

@wdfk-prog

Copy link
Copy Markdown
Contributor Author

结论 / Conclusion: Request changes

当前实现改变了 Generic CAN 与所有 BSP 之间的运行时契约,但只迁移了 STM32 bxCAN,因此暂不满足“其他 BSP 无需适配”的兼容性要求。

🔴 [Concurrency/并发]: Do not call un-migrated BSP send callbacks under irqsave / 不要在关中断锁内调用尚未迁移的 BSP 回调

中文:当前在 rt_spin_lock_irqsave() 保护区内调用 BSP sendmsg(),但现有 BSP 并不都满足“立即返回、不可等待”的新契约。HPMicro 调用 rt_thread_mdelay(),NS800 等待 TX ISR 更新状态,HT32 忙等邮箱。这会造成关中断睡眠,或因 TX ISR 无法执行而死锁。必须同步迁移所有后端,或增加 RESERVING 状态,让硬件提交在锁外执行。

🟠 [Lifecycle/生命周期]: Final close now requires every BSP to implement START(0) / 最终关闭强制要求所有 BSP 实现 START(0)

中文:rt_device_close() 进入驱动回调前已经把 ref_count 减为 0;这里若 BSP 不支持 RT_CAN_CMD_START(0),会在释放 RX/TX runtime 之前返回。现有 Nuvoton、Renesas、HT32 等后端会拒绝该命令,导致设备处于 ref_count=0、runtime 未释放、TX admission 已关闭的残缺状态,后续 reopen 也无法恢复。需要保留旧的可选行为,或为所有 BSP 完成能力适配和 open-close-open 回归。

中文:发送超时只代表等待超时,并不代表硬件已经结束请求。当前代码却执行 pending-- 并将 slot 设为 FREE。随后 drain/reconfigure 会错误判断 TX 已空闲;旧帧仍可能发送,迟到的 terminal 还可能冲掉其他 nonblocking 请求的 pending。应保留 TIMED_OUT/ABANDONED ownership,直到真实 TX_DONE/TX_FAIL 到来。

🟠 [Compatibility/兼容性]: Existing BSPs lose status polling / 现有 BSP 默认失去状态轮询
中文:仓库中 30 个启用 CAN 的 .config/rtconfig.h 都没有新宏。普通 SCons 构建不会重新计算 Kconfig 的 default y,因此现有 BSP 均不会编译状态轮询路径,并会在 RT_CAN_CMD_SET_STATUS_IND 返回 -RT_ENOSYS。需要保留原有默认行为,或通过配置工具迁移全部相关 BSP。

  • 还真有BSP,在发送里面做阻塞延时啊 😄
  • 那我在修改一版,重新做成CAN_V2框架不兼容现有BSP?
  • 发送超时只代表等待超时,并不代表硬件已经结束请求。当前代码却执行 pending-- 并将 slot 设为 FREE。
    • 这个问题考虑过,框架层需要处理的话,逻辑复杂了,所以设计中考虑的是超时时间交给用户设置,用户得保证这个超时时间一定是呃能够等到硬件发送完成的,如果到达超时时间反馈过来的一定是硬件发送完成或者已经知道失败了
  • 仓库中 30 个启用 CAN 的 .config/rtconfig.h 都没有新宏。这个不重要,只是默认缺少了canstata的msh命令功能,这样可以减少代码体积

@CYFS3

CYFS3 commented Sep 3, 2026

Copy link
Copy Markdown
Contributor
  • 那我在修改一版,重新做成CAN_V2框架不兼容现有BSP?

我感觉就不要在维护一套can_V2了,这样维护起来太麻烦了。看看有没有人感兴趣一起来push这个pr吧

@CYFS3

CYFS3 commented Sep 7, 2026

Copy link
Copy Markdown
Contributor
  • 那我在修改一版,重新做成CAN_V2框架不兼容现有BSP?

我感觉就不要在维护一套can_V2了,这样维护起来太麻烦了。看看有没有人感兴趣一起来push这个pr吧

也许可以在主仓新建个分支,先合到那个分支,我可以来一起推动这个pr,手头上有其他板子。然后也可以找社区的小伙伴来一起验证一下。最后没有问题在合并到master。

@wdfk-prog

Copy link
Copy Markdown
Contributor Author
  • 那我在修改一版,重新做成CAN_V2框架不兼容现有BSP?

我感觉就不要在维护一套can_V2了,这样维护起来太麻烦了。看看有没有人感兴趣一起来push这个pr吧

也许可以在主仓新建个分支,先合到那个分支,我可以来一起推动这个pr,手头上有其他板子。然后也可以找社区的小伙伴来一起验证一下。最后没有问题在合并到master。

  • 可以的,需要有一个必须测试的BSP列表和测试功能?

@CYFS3

CYFS3 commented Sep 8, 2026

Copy link
Copy Markdown
Contributor
  • 那我在修改一版,重新做成CAN_V2框架不兼容现有BSP?

我感觉就不要在维护一套can_V2了,这样维护起来太麻烦了。看看有没有人感兴趣一起来push这个pr吧

也许可以在主仓新建个分支,先合到那个分支,我可以来一起推动这个pr,手头上有其他板子。然后也可以找社区的小伙伴来一起验证一下。最后没有问题在合并到master。

  • 可以的,需要有一个必须测试的BSP列表和测试功能?

这一块可以ai扫一下目前有对接can的bsp,然后测试功能可以把utest也完善一下。

@wdfk-prog

Copy link
Copy Markdown
Contributor Author

Contributor

  • 抱歉,近期应该没有余力做这块工作了.我还可以协助代码的修改和审查;但是实际验证测试暂时是没有这个条件环境进行实验了

@BernardXiong

Copy link
Copy Markdown
Member
  • 那我在修改一版,重新做成CAN_V2框架不兼容现有BSP?

我感觉就不要在维护一套can_V2了,这样维护起来太麻烦了。看看有没有人感兴趣一起来push这个pr吧

也许可以在主仓新建个分支,先合到那个分支,我可以来一起推动这个pr,手头上有其他板子。然后也可以找社区的小伙伴来一起验证一下。最后没有问题在合并到master。

  • 可以的,需要有一个必须测试的BSP列表和测试功能?

建议最好就不再维护一个新的v2版本了,有多个版本后续维护上也挺麻烦的。建议是可以对主要的BSP做一定的适配,而另外一些BSP(使用不多或缺乏维护),则暂时先放一放,做一个对应标记或说明。

@CYFS3

CYFS3 commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

Contributor

  • 抱歉,近期应该没有余力做这块工作了.我还可以协助代码的修改和审查;但是实际验证测试暂时是没有这个条件环境进行实验了

先review现在这个框架有没有需要改进的,适配后续可以继续推进

@Rbb666 Rbb666 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

共 11 条行内意见。同步 TX_DONE 丢失、阻塞写超时后 drain() 提前返回这两项已用主机桩复现。其余意见来自源码和工程清单核对。

* very fast terminal cannot retire an unpublished blocking owner.
*/
rt_completion_init(&slot->completion);
result = can->ops->sendmsg(can, message, mailbox);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 · 已用主机桩复现。NXP MCXA 的 sendmsg() 会在返回前同步上报 TX_DONE。此时槽位仍为 FREE,单核下会丢掉完成事件,帧已发出但 write() 返回超时。建议在调用 BSP 前登记槽位,并兼容同步回调,避免持 tx_lock 调用可能阻塞的 BSP。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这部分倾向于由 BSP 适配新框架的发送契约,而不是在 Generic CAN 中增加同步完成的兼容状态。

当前设计要求 sendmsg() 为立即提交接口:不得等待硬件发送完成、不得在关中断期间休眠,也不应在接口返回前同步上报 TX_DONE/TX_FAIL。

STM32 bxCAN 按该模式实现。NXP MCXA 的同步完成上报,以及 HPMicro、HT32 等后端的等待行为,应分别在对应 BSP 中调整。同时需要明确该契约及后端迁移范围,避免继续将旧行为视为受支持的接口语义。

Comment thread components/drivers/can/can_tx.c Resolved
Comment thread components/drivers/can/can_rx.c Resolved
Comment thread components/drivers/can/dev_can.c Resolved
}

if (can->ops->sendmsg_nonblocking == RT_NULL)
result = can->ops->control(can, RT_DEVICE_CTRL_SET_INT,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1。Renesas RA 的 ra_can_control() 未实现 RT_DEVICE_CTRL_SET_INT,默认返回 -RT_ERROR。即使没有请求 RX/TX 中断,每次打开也会在这里使能错误中断并失败。请补齐 RA 的中断控制,或为采用其他中断初始化方式的 BSP 保留兼容路径。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这属于 BSP 对新 Generic CAN 接口契约的适配问题,不建议在 Generic 中针对特定芯片增加兼容分支。

当前 integration 分支的 ra_can_control() 已包含 RX、TX 和错误中断的 SET_INT/CLR_INT 处理,而 PR head 中的 RA 实现仍然较旧。

建议将 integration 中已经完成的 RA 中断控制适配同步至 PR,并核对具体硬件中断使能行为

Comment thread components/drivers/can/dev_can.c Resolved
Comment thread components/drivers/can/SConscript Resolved
Comment thread components/drivers/can/dev_can.c Resolved
return -RT_ENOSYS;
}

return can->ops->control(can, RT_CAN_CMD_ABORT_TX,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2。STM32 FDCAN 和 GD32 的 control() 遇到未知命令会返回 RT_EOK,但都没有实现 ABORT_TX。直接透传会让 ABORT_TX 和 ABORT_ALL 报告成功,而帧仍可能发出。建议显式检查 BSP 的取消能力,未实现时返回 -RT_ENOSYS。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

同意这属于 BSP 控制命令适配问题。

对于 STM32 FDCAN、GD32 等尚未实现硬件 ABORT 的后端,应明确返回 -RT_ENOSYS,不能在未取消硬件发送的情况下返回 RT_EOK。

具备硬件取消能力的后端应实现取消操作,并按当前 Generic CAN 契约在取消完成后上报相应 terminal。

Generic 层继续负责请求转发与 ownership 管理,不针对特定芯片增加额外取消流程。

Comment thread components/drivers/can/can_tx.c Resolved
@wdfk-prog

wdfk-prog commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor Author

共 11 条行内意见。同步 TX_DONE 丢失、阻塞写超时后 drain() 提前返回这两项已用主机桩复现。其余意见来自源码和工程清单核对。

  • 有些是需要去修改芯片驱动还做适配支持的,目前只有STM32进行了适配支持并验证;
  • 太多了,适配不过来.靠我一个人只能尽力而为慢慢来吧
顺序 适配层级 芯片/控制器 难度 代码依据与最小适配重点
01 指定首位 STM32 FDCAN(ARM Cortex-M;优先单核 STM32G4 等) 4/5 无 NB;Generic 配置 1 slot,但驱动配置 3 个 TX Buffer;核实 BufferIndexes 位图/回调索引、FIFO 模式和 START(0) 停止语义。
02 STM32 相近厂商 GD32F4/F5(先 F4 经典 CAN)(ARM 单核;F4 M4 / F5 M33) 3/5(F4) 已有旧版 NB;自动发送要返回 HAL 选中邮箱、避开 sndchange;修正忙检查与 -RT_EBUSY。F5 部分型号是 CAN FD,不能无证据复用 F4 经典邮箱逻辑。
03 bxCAN 类三邮箱 AT32 经典 CAN(ARM Cortex-M 单核) 2/5 已有 0/1/2 TX terminal;新增 BSP 自选可用邮箱和输出索引,校验已结算但未回收的 sndchange 邮箱。
04 bxCAN 类三邮箱 N32Gxx/Lxx(ARM Cortex-M 单核) 2/5 有 TSTS_TMEM0/1/2 检查及逐邮箱 TX 事件;补 NB 选箱与 busy 返回,避免软件 slot 仍被阻塞者占用。
05 bxCAN 类三邮箱 APM32 经典 CAN(ARM Cortex-M 单核) 2–3/5 有逐邮箱 TX_DONE/TX_FAIL;补 NB、自选箱/返回编号、发送忙判定,注意原 sendmsg 的错误码。
06 bxCAN 类三邮箱 ES32F369x(ARM Cortex-M 单核候选) 2–3/5 已有带邮箱编号的终端上报;先核对硬件三邮箱释放条件,再补独立非阻塞提交。
07 bxCAN 类三邮箱 ES32F0654(ARM Cortex-M 单核候选) 2–3/5 与 ES32F369x 实现接近;复用已验证的邮箱/终端适配思路,但核对具体寄存器差异。
08 bxCAN 类三邮箱 WCH CH32V(RISC-V)(单核 RISC-V;CAN 模型与 bxCAN 近似) 2–3/5 有 TSTATR_TME0/1/2、RQCP0/1/2 和逐邮箱事件;外设模型相近,因此按你的“同 CAN 设计优先”例外提前。
09 M_CAN/FDCAN 类 HC32 MCAN(ARM Cortex-M 单核) 3–4/5 已有旧版 NB 但转调 sendmsg(0);检查专用 Buffer/FIFO、Tx event 实际索引及即时返回,不能只增加 mailbox_out。
10 M_CAN/FDCAN 类 N32Hxxx FDCAN(先 N32H76x)(N32H76x 单 Cortex-M7;N32H78x 双核后置) 3–4/5 已有旧版 NB 扫描 Dedicated TX Buffer;补成功 buffer 索引及所有权过滤。对 N32H78 双核变体单独延后验证。
11 M_CAN/FDCAN 类 HPMicro MCAN(限确认的单核型号)(RISC-V;具体芯片核数待核实) 5/5 现有 sendmsg 在 TX FIFO 满时 mdelay 等待;Generic tx_lock 下不可睡眠。需区分 FIFO 物理 index/逻辑 slot、重审 START(0) 的旧终端抑制。
12 FlexCAN 同族 NXP MCX A(FlexCAN)(ARM Cortex-M 单核) 3–4/5 已有旧版 NB,会选择实际 MB;但 Generic sndboxnumber=1,不能把物理 MB 非零编号直接当 slot;需要一致映射及 ISR terminal。
13 FlexCAN 同族 NXP MCX N(先 N24)(N24 单核 Cortex-M33;N94/N54 等双核后置) 4/5 无 NB;当前仅 1 个 Generic slot,需核对 FlexCAN 物理 MB 选择、异步事件 index 和取消时序。
14 FlexCAN 同族 NXP i.MX RT(先单核型号)(ARM Cortex-M;部分型号双核后置) 4/5 无 NB;配置最多 16 个逻辑发送 slot,关注物理 MB/中断索引换算及不同系列外设差异。
15 FlexCAN 同族 Novosns NS800(暂定位置)(核心架构与核数待确认) 5/5 MB0 专用,有旧 NB,但底层 _can_sendBlocking 最多 mdelay 50 ms;START 分支为 no-op。核数未证实时不应先实施,若为多核移到队尾。
16 其他 ARM 单核 MCU HC32 经典 CAN/PTB(ARM Cortex-M 单核) 2–3/5 旧 NB 直接 sendmsg(0),PTB 单发送缓冲;可固定返回逻辑 0,但必须检查忙状态与硬件事件。
17 其他 ARM 单核 MCU HT32 CAN(ARM Cortex-M 单核) 2–3/5 无 NB,单发送缓冲/单事件模型;明确硬件忙时立即 -RT_EBUSY,成功返回逻辑 0。
18 其他 ARM 单核 MCU Synwit SWM341(ARM Cortex-M 单核) 3/5 无 NB;发送函数直接写帧寄存器且缺独立忙判定,需要先验证 TX buffer 可用与 ISR 一次性事件。
19 其他 ARM 单核 MCU Renesas RA(ARM Cortex-M 单核) 3/5 无 NB;Generic 只配置 1 slot,回调使用物理 mailbox;按实际发送路径统一逻辑索引与异步事件。
20 其他 ARM 单核 MCU AT32 CAN v2(PTB/STB)(ARM Cortex-M 单核) 4/5 不是旧款三邮箱模型;sendmsg 走 CAN_TXBUF_STB/排队,且另有等待循环;必须先建立一请求一 terminal 的逻辑 slot 映射。
21 其他 ARM 单核 MCU Nuvoton M2354(ARM Cortex-M 单核) 4/5 无 NB;CAN 消息对象中断未直接上报 TX slot,先核实消息对象编号与 Generic slot 的映射。
22 其他 ARM 单核 MCU Nuvoton M480(ARM Cortex-M 单核) 4/5 无 NB;消息对象发送与部分 TX_DONE 事件未编码邮箱编号,需统一 sendmsg(boxno) 与 ISR 对应关系。
23 其他 ARM 单核 MCU Nuvoton M460 CAN FD(ARM Cortex-M 单核) 4/5 无 NB;CAN FD 消息 RAM/发送事件及 buffer 逻辑编号须独立核对,不直接套用 M480 CAN 2.0。
24 ARM 单核 MPU Nuvoton N9H30(ARM9)(ARM9 单核 MPU) 4/5 与 NuMicro 发送对象实现同族;优先复用 M480/M2354 核对后的消息对象与 terminal 映射。
25 ARM 单核 MPU Nuvoton NUC980(ARM9)(ARM9 单核 MPU) 4/5 与 N9H30 同组,需重新确认控制器状态/缓存/IRQ 行为后适配。
26 其他架构单核 HPMicro CAN(限确认的单核型号)(RISC-V 单核型号优先) 3–4/5 无 NB;现有 TX_DONE 逻辑索引为 0,先核查硬件忙与当前发送函数是否直接等待,及 STOP 的抑制语义。
27 其他架构单核 Loongson LS1C(MIPS 单核) 3–4/5 无 NB;原 sndboxnumber=1 且终端索引 0,优先补单邮箱即时提交,另外核实停止能力。
28 多核/异构平台最后 Nuvoton MA35(CAN FD)(双 Cortex-A35 + Cortex-M4) 4/5 无 NB;发送逻辑 slot 与 Message RAM/ISR 对应需要跨处理域和运行模式核验。
29 多核/异构平台最后 Rockchip CAN FD(ARM 多核 MPU/SoC) 4/5 无 NB;当前 TX_DONE 中包含索引和无索引路径,先判明中断运行域及可用 buffer 个数。
30 多核/异构平台最后 Phytium CAN(ARM64 SoC;核数依具体型号) 4/5 无 NB;原 sndboxnumber=1 且终端多未显式编号。若目标明确为单核变体,可以重排至 ARM 单核 MPU 阶段。
31 多核/异构平台最后 FT2004(FT-2000/4)(四核 ARMv8 兼容 SoC) 5/5 无 NB;扫描的驱动内未找到显式 RT_CAN_EVENT_TX_DONE/FAIL,先补充或核实真实 terminal 事件契约。
32 多核/异构平台最后 RDK S100(6×Cortex-A78AE + 4×Cortex-R52+) 4–5/5 旧 NB 依次尝试物理箱但不返回索引;多处理域及 TX event 映射,最后实施。

- 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.

@BernardXiong BernardXiong left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI 在 head faaeee70 失败,属代码问题,先同步结果:

  1. 编译失败(多个 BSP):components/drivers/can/dev_can.c:675 调用了 rt_can_tx_retire_after_stop(),但 components/drivers/can/can_internal.h 中没有该函数的声明,can_tx.c:306 只有定义。-Werror=implicit-function-declaration 导致 aarch64-bsp-smart、gd32_n32_apm32、novosns、nxp_renesas 等构建失败。

  2. 格式检查失败:共 61 处 clang-format 违规,涉及 bsp/stm32/libraries/HAL_Drivers/drivers/drv_can.c、can_internal.h、can_rx.c、can_tx.c、dev_can.c、dev_can.h。请使用仓库固定的 clang-format 22.1.8 处理。

  3. doxygen 失败:components/drivers/include/drivers/dev_can.h:849 报 end of file while inside a group,请补上分组结束标记。

本轮先不做完整代码 review;修复上述问题、CI 通过后再基于新 head 复查。

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants