CTU CAN FD 驱动程序

作者:Martin Jerabek <martin.jerabek01@gmail.com>

关于 CTU CAN FD IP 核

CTU CAN FD 是用 VHDL 编写的开源软核。它起源于 2015 年,是 Ondrej Ille 在 测量系FEECTU 的项目。

基于 Xilinx Zynq SoC 的 MicroZed 板的 SocketCAN 驱动程序 Vivado 集成 和基于 Intel Cyclone V 5CSEMA4U23C6 的 DE0-Nano-SoC Terasic 板的 QSys 集成 已经开发出来,并且支持该内核的 PCIe 集成

对于 Zynq,该内核通过 APB 系统总线连接,该总线不支持枚举,并且必须在设备树中指定该设备。这种设备在内核中称为平台设备,并由平台设备驱动程序处理。

CTU CAN FD 外设的基本功能模型已被 QEMU 主线接受。请参阅 QEMU CAN 仿真支持,了解 CAN FD 总线、主机连接和 CTU CAN FD 内核仿真。仿真支持的开发版本可以从 QEMU 本地开发的 ctu-canfd 分支中克隆 仓库

关于 SocketCAN

SocketCAN 是 Linux 内核中 CAN 设备的标准通用接口。顾名思义,该总线通过套接字访问,类似于常见的网络设备。其背后的原因在 Linux SocketCAN 中进行了深入描述。简而言之,它提供了一种自然的方式来实现和处理 CAN 上的更高层协议,就像以太网上面的 UDP/IP 一样。

设备探测

在详细介绍 CAN 总线设备驱动程序的结构之前,让我们重申一下内核是如何得知该设备的。一些总线(如 PCI 或 PCIe)支持设备枚举。也就是说,当系统启动时,它会发现总线上的所有设备并读取它们的配置。内核通过其供应商 ID 和设备 ID 来识别设备,并且如果为该标识符组合注册了驱动程序,则会调用其探测方法来填充给定硬件的驱动程序实例。USB 的情况类似,只是它允许设备热插拔。

对于直接嵌入到 SoC 中并连接到内部系统总线(AXI、APB、Avalon 等)的外设,情况有所不同。这些总线不支持枚举,因此内核必须从其他地方了解这些设备。这正是设备树的用途。

设备树

设备树中的一个条目声明系统中存在一个设备,它的可访问方式(它位于哪个总线上)以及它的配置——寄存器地址、中断等等。此类设备树的示例如下。

/ {
    /* ... */
    amba: amba {
        #address-cells = <1>;
        #size-cells = <1>;
        compatible = "simple-bus";

        CTU_CAN_FD_0: CTU_CAN_FD@43c30000 {
            compatible = "ctu,ctucanfd";
            interrupt-parent = <&intc>;
            interrupts = <0 30 4>;
            clocks = <&clkc 15>;
            reg = <0x43c30000 0x10000>;
        };
    };
};

驱动程序结构

驱动程序可以分为两个部分——平台相关的设备发现和设置,以及平台无关的 CAN 网络设备实现。

平台设备驱动程序

对于 Zynq,该内核通过 AXI 系统总线连接,该总线不支持枚举,并且必须在设备树中指定该设备。这种设备在内核中称为平台设备,并由平台设备驱动程序处理 [1]

平台设备驱动程序提供以下内容

  • 一个探测函数

  • 一个移除函数

  • 一个驱动程序可以处理的兼容设备表

当设备出现时(或驱动程序被加载时,以较晚者为准),探测函数只会被调用一次。如果同一驱动程序处理多个设备,则会为每个设备调用探测函数。它的作用是分配和初始化处理设备所需的资源,以及为平台无关层设置底层函数,例如 read_regwrite_reg。之后,驱动程序将设备注册到更高的层,在本例中为网络设备

当设备消失时,或者驱动程序即将卸载时,将调用移除函数。它的作用是释放在探测中分配的资源,并将设备从更高的层中注销。

最后,兼容设备表声明驱动程序可以处理哪些设备。设备树条目 compatible 与所有平台驱动程序的表进行匹配。

/* Match table for OF platform binding */
static const struct of_device_id ctucan_of_match[] = {
    { .compatible = "ctu,canfd-2", },
    { .compatible = "ctu,ctucanfd", },
    { /* end of list */ },
};
MODULE_DEVICE_TABLE(of, ctucan_of_match);

static int ctucan_probe(struct platform_device *pdev);
static int ctucan_remove(struct platform_device *pdev);

static struct platform_driver ctucanfd_driver = {
    .probe  = ctucan_probe,
    .remove = ctucan_remove,
    .driver = {
        .name = DRIVER_NAME,
        .of_match_table = ctucan_of_match,
    },
};
module_platform_driver(ctucanfd_driver);

网络设备驱动程序

每个网络设备必须至少支持以下操作

  • 启动设备:ndo_open

  • 关闭设备:ndo_close

  • 将 TX 帧提交到设备:ndo_start_xmit

  • 向网络子系统发出 TX 完成和错误信号:ISR

  • 将 RX 帧提交到网络子系统:ISR 和 NAPI

有两种可能的事件源:设备和网络子系统。设备事件通常通过中断发出信号,并在中断服务例程 (ISR) 中处理。然后,在 struct net_device_ops 中指定网络子系统中发生的事件的处理程序。

当设备启动时,例如通过调用 ip link set can0 up,将调用驱动程序的函数 ndo_open。它应该验证接口配置并配置和启用设备。与此相反的是 ndo_close,当设备被关闭时调用,无论是显式地还是隐式地。

当系统应该传输一个帧时,它会通过调用 ndo_start_xmit 来完成,该函数将帧排队到设备中。如果设备 HW 队列(FIFO、邮箱或任何实现)已满,则 ndo_start_xmit 实现会通知网络子系统,它应该停止 TX 队列(通过 netif_stop_queue)。然后在 ISR 中再次重新启用它,当设备有可用空间并且能够再次将另一个帧排队时。

所有设备事件都在 ISR 中处理,即

  1. TX 完成。当设备成功完成帧的传输时,该帧会在本地回显。如果发生错误,则会向网络子系统发送一个信息错误帧 [2]。在这两种情况下,都会恢复软件 TX 队列,以便可以发送更多帧。

  2. 错误条件。如果出现问题(例如,设备进入总线关闭状态或发生 RX 溢出),则会更新错误计数器,并将信息错误帧排队到 SW RX 队列。

  3. RX 缓冲区不为空。在这种情况下,读取 RX 帧并将它们排队到 SW RX 队列。通常使用 NAPI 作为中间层(请参阅)。

NAPI

传入帧的频率可能很高,并且为每个帧调用中断服务例程的开销可能会导致显着的系统负载。Linux 内核中有多种机制可以处理这种情况。它们是在 Linux 内核开发和增强的多年中演变而来的。对于网络设备,当前的标准是 NAPI——New API。它类似于经典的上半部/下半部中断处理,因为它只在 ISR 中确认中断,并发出信号表示其余处理应在 softirq 上下文中完成。最重要的是,它提供了轮询一段时间新帧的可能性。这有可能避免代价高昂的启用中断、在 ISR 中处理传入 IRQ、重新启用 softirq 和将上下文切换回 softirq 的过程。

有关更多信息,请参阅 Documentation/networking/napi.rst

将内核集成到 Xilinx Zynq

该内核与 Avalon(搜索 Intel Avalon Interface Specifications)总线的一个简单子集接口,因为它最初是在 Alterra FPGA 芯片上使用的,但 Xilinx 原生与 AXI 接口(搜索 ARM AMBA AXI and ACE Protocol Specification AXI3, AXI4, and AXI4-Lite, ACE and ACE-Lite)。最明显的解决方案是使用 Avalon/AXI 桥接器或实现一些简单的转换实体。然而,该内核的接口是半双工的,没有握手信令,而 AXI 是全双工的,具有双向信令。此外,即使是 AXI-Lite 从接口也相当消耗资源,并且 CAN 内核不需要 AXI 的灵活性和速度。

因此,选择了一个更简单的总线——APB(Advanced Peripheral Bus)(搜索 ARM AMBA APB Protocol Specification)。APB-AXI 桥接器直接在 Xilinx Vivado 中可用,并且接口适配器实体只是一些简单的组合赋值。

最后,为了能够将该内核作为自定义 IP 包含在框图中,该内核与 APB 接口一起被打包为 Vivado 组件。

CTU CAN FD 驱动程序设计

CAN 设备驱动程序的一般结构已在 中进行了检查。接下来的段落将提供有关 CTU CAN FD 内核驱动程序的更详细描述。

底层驱动程序

该内核并非仅供 SocketCAN 使用,因此需要有一个与操作系统无关的底层驱动程序。然后,此底层驱动程序可以用于操作系统驱动程序的实现,或者直接用于裸机或用户空间应用程序。另一个优点是,如果硬件略有变化,则只需要修改底层驱动程序。

代码 [3] 部分是自动生成的,部分是由内核作者手动编写的,并由论文作者贡献。底层驱动程序支持诸如以下操作:设置位时序、设置控制器模式、启用/禁用、读取 RX 帧、写入 TX 帧等等。

配置位时序

在 CAN 上,每个位分为四个段:SYNC、PROP、PHASE1 和 PHASE2。它们的持续时间以时间量子 (Time Quantum) 的倍数表示(详情请参见 CAN Specification, Version 2.0,第 8 章)。配置比特率时,必须从比特率和采样点计算所有段(和时间量子)的持续时间。对于 CAN FD,这是独立于标称比特率和数据比特率执行的。

SocketCAN 非常灵活,可以通过手动设置所有段持续时间来实现高度自定义配置,或者通过仅设置比特率和采样点来实现方便的配置(即使未指定,也可以按照 Bosch 建议自动选择)。但是,每个 CAN 控制器可能具有不同的基本时钟频率和不同的段持续时间寄存器宽度。因此,该算法需要持续时间的最小值和最大值(以及时钟预分频器),并尝试优化数字以适应约束和请求的参数。

struct can_bittiming_const {
    char name[16];      /* Name of the CAN controller hardware */
    __u32 tseg1_min;    /* Time segment 1 = prop_seg + phase_seg1 */
    __u32 tseg1_max;
    __u32 tseg2_min;    /* Time segment 2 = phase_seg2 */
    __u32 tseg2_max;
    __u32 sjw_max;      /* Synchronisation jump width */
    __u32 brp_min;      /* Bit-rate prescaler */
    __u32 brp_max;
    __u32 brp_inc;
};

[lst:can_bittiming_const]

好奇的读者会注意到,段 PROP_SEG 和 PHASE_SEG1 的持续时间不是单独确定的,而是组合在一起,然后默认情况下,生成的 TSEG1 在 PROP_SEG 和 PHASE_SEG1 之间均匀分配。实际上,这几乎没有后果,因为采样点在 PHASE_SEG1 和 PHASE_SEG2 之间。但是,在 CTU CAN FD 中,持续时间寄存器 PROPPH1 具有不同的宽度(分别为 6 位和 7 位),因此自动计算的值可能会溢出较短的寄存器,因此必须在两个之间重新分配 [4]

处理 RX

帧接收在 NAPI 队列中处理,当设置 RXNE(RX FIFO 不为空)位时,从 ISR 启用该队列。逐个读取帧,直到 RX FIFO 中没有剩余帧或达到 NAPI 轮询运行的最大工作配额(请参阅)。然后将每个帧传递到网络接口 RX 队列。

传入帧可以是 CAN 2.0 帧或 CAN FD 帧。在内核中区分这两者的方法是分配 struct can_framestruct canfd_frame,两者具有不同的大小。在控制器中,有关帧类型的信息存储在 RX FIFO 的第一个字中。

这给我们带来了一个先有鸡还是先有蛋的问题:我们想要为帧分配 skb,只有在成功的情况下才从 FIFO 获取帧;否则将其保留在那里以供以后使用。但是为了能够分配正确的 skb,我们必须获取 FIFO 的第一个字。有几种可能的解决方案

  1. 读取该字,然后分配。如果失败,则丢弃帧的其余部分。当系统内存不足时,情况无论如何都很糟糕。

  2. 始终预先分配足够大的 skb 以用于 FD 帧。然后调整 skb 的内部结构,使其看起来像是为较小的 CAN 2.0 帧分配的。

  3. 添加选项以窥视 FIFO 而不是消耗该字。

  4. 如果分配失败,则将读取的字存储到驱动程序的数据中。在下一次尝试时,使用存储的字而不是再次读取它。

选项 1 足够简单,但如果我们做得更好,就不会那么令人满意。选项 2 是不可接受的,因为它需要修改完整内核结构的私有状态。稍微更高的内存消耗只是“蛋糕”上的虚拟樱桃。选项 3 需要非平凡的 HW 更改,并且从 HW 的角度来看并不理想。

选项 4 似乎是一个很好的折衷方案,它的缺点是部分帧可能会在 FIFO 中停留很长时间。尽管如此,可能只有一个 RX FIFO 的所有者,因此没有人应该看到部分帧(忽略一些特殊的调试场景)。此外,驱动程序会在初始化时重置内核,因此部分帧也不能被“采用”。最终,选择了选项 4 [5]

时间戳 RX 帧

CTU CAN FD 内核报告接收到帧时的确切时间戳。默认情况下,时间戳是在 EOF 的最后一位的采样点捕获的,但可以配置为在 SOF 位捕获。时间戳源在内核外部,最大宽度可达 64 位。在编写本文时,尚未实现将时间戳从内核传递到用户空间,但计划在将来实现。

处理 TX

CTU CAN FD 内核具有 4 个独立的 TX 缓冲区,每个缓冲区都有自己的状态和优先级。当内核想要传输时,将选择处于 Ready 状态且优先级最高的 TX 缓冲区。

优先级是寄存器 TX_PRIORITY 中的 3 位数字(字节对齐)。这对于大多数用例来说应该足够灵活。但是,SocketCAN 仅支持一个用于传出帧的 FIFO 队列 [6]。可以通过为每个缓冲区分配一个不同的优先级并在帧传输完成后旋转优先级来使用缓冲区优先级来模拟 FIFO 行为。

除了优先级旋转之外,软件还必须维护由 TX 缓冲区形成的 FIFO 中的头指针和尾指针,以便能够确定哪个缓冲区应该用于下一个帧 (txb_head) 以及哪个应该是第一个完成的缓冲区 (txb_tail)。实际的缓冲区索引(显然)是模 4(TX 缓冲区的数量),但指针必须至少宽一位,才能区分 FIFO 已满和 FIFO 为空——在这种情况下,txb\_head \equiv txb\_tail\ (\textrm{mod}\ 4)。如何在维护 FIFO 的同时进行优先级旋转的示例,请参见


TXB#

0

1

2

3

Seq

A

B

C

Prio

7

6

5

4

T

H


TXB#

0

1

2

3

Seq

B

C

Prio

4

7

6

5

T

H


TXB#

0

1

2

3

0’

Seq

E

B

C

D

Prio

4

7

6

5

T

H


../../../../_images/fsm_txt_buffer_user.svg

TX 缓冲区状态以及可能的转换

时间戳 TX 帧

当将帧提交到 TX 缓冲区时,可以指定应该传输帧的时间戳。帧传输可能会稍后开始,但不会更早。请注意,时间戳不参与缓冲区优先级排序——这完全由上述机制决定。

对基于时间的包传输的支持最近已合并到 Linux v4.19 中 基于时间的包传输,但仍有待研究此功能是否对 CAN 实用。

同样,与检索 RX 帧的时间戳类似,内核支持检索 TX 帧的时间戳——这是成功传递帧的时间。细节与时间戳 RX 帧非常相似,并在 中进行了描述。

处理 RX 缓冲区溢出

当接收到的帧不再完全适合硬件 RX FIFO 时,会设置 RX FIFO 溢出标志 (STATUS[DOR]) 并触发数据溢出中断 (DOI)。在处理中断时,必须首先清除 DOR 标志(通过 COMMAND[CDO]),然后清除 DOI 中断标志。否则,中断将立即 [7] 重新激活。

注意:在开发过程中,有人讨论了内部 HW 流水线是否会中断此清除序列,以及在清除标志和中断之间是否需要额外的虚拟周期。在 Avalon 接口上,情况确实如此,但 APB 是安全的,因为它使用 2 个周期的事务。基本上,DOR 标志将被清除,但当 DOI 清除请求也将被应用时(通过将寄存器的 Reset 输入设置为高电平),DOI 寄存器的 Preset 输入仍然很高。由于 Set 的优先级高于 Reset,因此 DOI 标志不会被重置。这已经通过交换 Set/Reset 优先级来解决(请参阅问题 #187)。

报告错误被动和总线关闭条件

可能需要报告节点何时达到错误被动错误警告总线关闭条件。驱动程序通过中断(EPI、EWLI)通知错误状态更改,然后通过读取其错误计数器来确定内核的错误状态。

但是,这里存在一个轻微的竞争条件——在状态转换发生(并且触发中断)和读取错误计数器之间存在延迟。当收到 EPI 时,节点可能处于错误被动总线关闭状态。如果节点进入总线关闭状态,则显然会保持该状态直到重置。否则,该节点处于或曾经处于错误被动状态。但是,可能会发生读取状态为错误警告甚至错误活动状态。在那种情况下,可能不清楚是否以及究竟要报告什么,但我个人认为应该仍然报告过去的错误条件。同样,当收到 EWLI 但后来检测到状态为错误被动时,应报告错误被动状态。

CTU CAN FD 驱动程序源代码参考

int ctucan_probe_common(struct device *dev, void __iomem *addr, int irq, unsigned int ntxbufs, unsigned long can_clk_rate, int pm_enable_call, void (*set_drvdata_fnc)(struct device *dev, struct net_device *ndev))

设备类型无关注册调用

参数

struct device *dev

通用设备结构的句柄

void __iomem *addr

CTU CAN FD 内核地址的基地址

int irq

中断号

unsigned int ntxbufs

已实现的 Tx 缓冲区的数量

unsigned long can_clk_rate

时钟速率,如果为 0,则从设备节点获取时钟

int pm_enable_call

是否应该调用 pm_runtime_enable

void (*set_drvdata_fnc)(struct device *dev, struct net_device *ndev)

用于设置物理设备的网络驱动程序数据的函数

描述

此函数执行 CAN 设备的所有内存分配和注册。

返回值

成功时返回 0,错误时返回失败值

const char *ctucan_state_to_str(enum can_state state)

将 CAN 控制器状态代码转换为相应的文本

参数

enum can_state state

CAN 控制器状态代码

返回值

指向错误状态字符串表示形式的指针

int ctucan_reset(struct net_device *ndev)

向 CTU CAN FD 发出软件重置请求

参数

struct net_device *ndev

指向 net_device 结构的指针

返回值

成功时返回 0,如果 CAN 控制器未离开重置状态,则返回 -ETIMEDOUT

int ctucan_set_btr(struct net_device *ndev, struct can_bittiming *bt, bool nominal)

在 CTU CAN FD 中设置 CAN 总线位时序

参数

struct net_device *ndev

指向 net_device 结构的指针

struct can_bittiming *bt

指向位时序结构的指针

bool nominal

True - 标称位时序,False - 数据位时序

返回值

0 - OK,如果控制器已启用,则返回 -EPERM

int ctucan_set_bittiming(struct net_device *ndev)

CAN 设置标称位时序例程

参数

struct net_device *ndev

指向 net_device 结构的指针

返回值

成功时返回 0,错误时返回 -EPERM

int ctucan_set_data_bittiming(struct net_device *ndev)

CAN 设置数据位时序例程

参数

struct net_device *ndev

指向 net_device 结构的指针

返回值

成功时返回 0,错误时返回 -EPERM

int ctucan_set_secondary_sample_point(struct net_device *ndev)

在 CTU CAN FD 中设置辅助采样点

参数

struct net_device *ndev

指向 net_device 结构的指针

返回值

成功返回 0,如果控制器已启用,则返回 -EPERM

void ctucan_set_mode(struct ctucan_priv *priv, const struct can_ctrlmode *mode)

设置 CTU CAN FD 的模式

参数

struct ctucan_priv *priv

指向私有数据的指针

const struct can_ctrlmode *mode

指向要设置的控制器模式的指针

int ctucan_chip_start(struct net_device *ndev)

此例程启动驱动程序

参数

struct net_device *ndev

指向 net_device 结构的指针

描述

例程期望芯片处于重置状态。它为 FIFO 优先级设置初始 Tx 缓冲区,设置位时序,启用中断,将核心切换到操作模式,并将控制器状态更改为 CAN_STATE_STOPPED

返回值

成功时返回 0,错误时返回失败值

int ctucan_do_set_mode(struct net_device *ndev, enum can_mode mode)

设置驱动程序的模式

参数

struct net_device *ndev

指向 net_device 结构的指针

enum can_mode mode

告知驱动程序的模式

描述

这检查驱动程序的状态并调用相应的模式进行设置。

返回值

成功时返回 0,错误时返回失败值

enum ctucan_txtb_status ctucan_get_tx_status(struct ctucan_priv *priv, u8 buf)

获取 TXT 缓冲区的状态

参数

struct ctucan_priv *priv

指向私有数据的指针

u8 buf

缓冲区索引(从 0 开始)

返回值

TXT 缓冲区的状态

bool ctucan_is_txt_buf_writable(struct ctucan_priv *priv, u8 buf)

检查是否可以将帧插入到 TXT 缓冲区

参数

struct ctucan_priv *priv

指向私有数据的指针

u8 buf

缓冲区索引(从 0 开始)

返回值

True - 可以将帧插入到 TXT 缓冲区,False - 如果尝试,帧将不会被

插入到 TXT 缓冲区

bool ctucan_insert_frame(struct ctucan_priv *priv, const struct canfd_frame *cf, u8 buf, bool isfdf)

将帧插入到 TXT 缓冲区

参数

struct ctucan_priv *priv

指向私有数据的指针

const struct canfd_frame *cf

指向要插入的 CAN 帧的指针

u8 buf

帧插入到的 TXT 缓冲区索引(从 0 开始)

bool isfdf

True - CAN FD 帧,False - CAN 2.0 帧

返回值

True - 帧插入成功
False - 由于以下原因之一,帧未插入
  1. TXT 缓冲区不可写(处于错误状态)

  2. 无效的 TXT 缓冲区索引

  3. 无效的帧长度

void ctucan_give_txtb_cmd(struct ctucan_priv *priv, enum ctucan_txtb_command cmd, u8 buf)

将命令应用于 TXT 缓冲区

参数

struct ctucan_priv *priv

指向私有数据的指针

enum ctucan_txtb_command cmd

要给出的命令

u8 buf

缓冲区索引(从 0 开始)

netdev_tx_t ctucan_start_xmit(struct sk_buff *skb, struct net_device *ndev)

开始传输

参数

struct sk_buff *skb

sk_buff 指针,包含要 Tx 的数据

struct net_device *ndev

指向 net_device 结构的指针

描述

从上层调用以启动传输。使用下一个可用的空闲 TXT 缓冲区并填充其字段以启动传输。

返回值

成功时返回 NETDEV_TX_OK,当没有可用的空闲 TXT 缓冲区时返回 NETDEV_TX_BUSY

负返回值保留用于错误情况

void ctucan_read_rx_frame(struct ctucan_priv *priv, struct canfd_frame *cf, u32 ffw)

从 RX FIFO 读取帧

参数

struct ctucan_priv *priv

指向 CTU CAN FD 的私有数据的指针

struct canfd_frame *cf

指向 CAN 帧结构的指针

u32 ffw

先前读取的帧格式字

注意

帧格式字必须单独读取并在“ffw”中提供。

int ctucan_rx(struct net_device *ndev)

从 CAN ISR 调用以完成接收帧处理

参数

struct net_device *ndev

指向 net_device 结构的指针

描述

此函数从 CAN isr(poll) 调用以处理 Rx 帧。它进行最小处理并调用“netif_receive_skb”以完成进一步处理。

返回值

当帧传递到网络层时返回 1,当读取第一个帧字时返回 0 但

系统暂时没有可用的 SKB,并留下代码稍后解决 SKB 分配问题,在 Rx FIFO 为空的情况下返回 -EAGAIN

enum can_state ctucan_read_fault_state(struct ctucan_priv *priv)

读取 CTU CAN FD 的故障限制状态。

参数

struct ctucan_priv *priv

指向私有数据的指针

返回值

控制器的故障限制状态

void ctucan_get_rec_tec(struct ctucan_priv *priv, struct can_berr_counter *bec)

从控制器读取 REC/TEC 计数器值

参数

struct ctucan_priv *priv

指向私有数据的指针

struct can_berr_counter *bec

指向错误计数器结构的指针

void ctucan_err_interrupt(struct net_device *ndev, u32 isr)

错误帧 ISR

参数

struct net_device *ndev

net_device 指针

u32 isr

中断状态寄存器值

描述

这是 CAN 错误中断,它将检查错误类型并将错误帧转发到上层。

int ctucan_rx_poll(struct napi_struct *napi, int quota)

rx 数据包的轮询例程 (NAPI)

参数

struct napi_struct *napi

NAPI 结构指针

int quota

要处理的最大 rx 数据包数。

描述

这是 rx 部分的轮询例程。它将处理最大配额值的数据包。

返回值

收到的数据包数

void ctucan_rotate_txb_prio(struct net_device *ndev)

旋转 TXT 缓冲区的优先级

参数

struct net_device *ndev

net_device 指针

void ctucan_tx_interrupt(struct net_device *ndev)

Tx 完成 Isr

参数

struct net_device *ndev

net_device 指针

irqreturn_t ctucan_interrupt(int irq, void *dev_id)

CAN Isr

参数

int irq

irq 编号

void *dev_id

设备 id 指针

描述

这是 CTU CAN FD ISR。它检查中断类型并调用相应的 ISR。

返回值

IRQ_NONE - 如果 CAN 设备处于睡眠模式,否则为 IRQ_HANDLED

void ctucan_chip_stop(struct net_device *ndev)

驱动程序停止例程

参数

struct net_device *ndev

指向 net_device 结构的指针

描述

这是驱动程序的停止例程。它将禁用中断并禁用控制器。

int ctucan_open(struct net_device *ndev)

驱动程序打开例程

参数

struct net_device *ndev

指向 net_device 结构的指针

描述

这是驱动程序打开例程。

返回值

成功时返回 0,错误时返回失败值

int ctucan_close(struct net_device *ndev)

驱动程序关闭例程

参数

struct net_device *ndev

指向 net_device 结构的指针

返回值

始终为 0

int ctucan_get_berr_counter(const struct net_device *ndev, struct can_berr_counter *bec)

错误计数器例程

参数

const struct net_device *ndev

指向 net_device 结构的指针

struct can_berr_counter *bec

指向 can_berr_counter 结构的指针

描述

这是驱动程序错误计数器例程。

返回值

成功时返回 0,错误时返回失败值

int ctucan_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)

PCI 注册调用

参数

struct pci_dev *pdev

PCI 设备结构的句柄

const struct pci_device_id *ent

指向 ctucan_pci_tbl 中的条目的指针

描述

此函数执行 CAN 设备的所有内存分配和注册。

返回值

成功时返回 0,错误时返回失败值

void ctucan_pci_remove(struct pci_dev *pdev)

释放资源后注销设备

参数

struct pci_dev *pdev

PCI 设备结构的句柄

描述

此函数释放分配给设备的所有资源。

返回值

始终为 0

int ctucan_platform_probe(struct platform_device *pdev)

平台注册调用

参数

struct platform_device *pdev

平台设备结构的句柄

描述

此函数执行 CAN 设备的所有内存分配和注册。

返回值

成功时返回 0,错误时返回失败值

void ctucan_platform_remove(struct platform_device *pdev)

释放资源后注销设备

参数

struct platform_device *pdev

平台设备结构的句柄

描述

此函数释放分配给设备的所有资源。

返回值

始终为 0

CTU CAN FD IP 核和驱动程序开发感谢

  • Intel SoC 的系统集成、核心和驱动程序测试和更新

  • 提供了 OSADL 专业知识来讨论 IP 核许可

  • 指出了 LGPL 和 CAN 总线可能的专利案例可能出现的死锁,这导致将 IP 核设计重新许可为类似 BSD 的许可证

  • 提供了建议和帮助,以告知社区有关该项目的信息,并邀请我们参加专注于 CAN 总线未来发展方向的活动

  • Jan Charvat

  • 为 QEMU 实现了 CTU CAN FD 功能模型,该模型已集成到 QEMU 主线中 (docs/system/devices/can.rst)

  • 学士论文 QEMU 仿真器的 CAN FD 通信控制器模型

注释