CTU CAN FD 驱动程序

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

关于 CTU CAN FD IP 核

CTU CAN FD 是一个用 VHDL 编写的开源软核。它起源于 2015 年 Ondrej Ille 在 测量系 的项目,该系隶属于 FEE ,位于 CTU

基于 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 来完成,该函数将帧排入设备。如果设备硬件队列(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 – 新 API。它类似于经典的上半部/下半部中断处理,因为它仅在 ISR 中确认中断,并发出信号,表明其余处理应在软中断上下文中完成。最重要的是,它提供了轮询一段时间新帧的可能性。这有可能避免启用中断、在 ISR 中处理传入的 IRQ、重新启用软中断并将上下文切换回软中断的代价高昂的循环。

有关详细信息,请参阅 Documentation/networking/napi.rst

将核心集成到 Xilinx Zynq

该核心接口了 Avalon(搜索 Intel Avalon 接口规范)总线的一个简单子集,因为它最初用于 Alterra FPGA 芯片,但 Xilinx 本机与 AXI(搜索 ARM AMBA AXI 和 ACE 协议规范 AXI3、AXI4 和 AXI4-Lite、ACE 和 ACE-Lite)接口。最明显的解决方案是使用 Avalon/AXI 桥或实现一些简单的转换实体。但是,核心的接口是半双工的,没有握手信号,而 AXI 是具有双向信号的全双工。此外,即使是 AXI-Lite 从接口也相当耗费资源,而 CAN 核心不需要 AXI 的灵活性和速度。

因此,选择了一个更简单的总线 – APB(高级外围总线)(搜索 ARM AMBA APB 协议规范)。APB-AXI 桥直接在 Xilinx Vivado 中可用,并且接口适配器实体只是一些简单的组合赋值。

最后,为了能够将核心作为自定义 IP 包含在模块图中,核心及其 APB 接口已打包为 Vivado 组件。

CTU CAN FD 驱动程序设计

CAN 设备驱动程序的总体结构已在 中检查过。接下来的段落提供了有关 CTU CAN FD 核心驱动程序的更详细描述。

底层驱动程序

该核心不打算仅与 SocketCAN 一起使用,因此需要有一个独立于操作系统的底层驱动程序。然后,此底层驱动程序可以在操作系统驱动程序的实现中使用,也可以直接在裸机或用户空间应用程序中使用。另一个优点是,如果硬件略有变化,则只需要修改底层驱动程序。

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

配置位时序

在 CAN 总线上,每个位被分为四个段:SYNC、PROP、PHASE1 和 PHASE2。它们的持续时间以时间量子的倍数表示(详情请参阅CAN 规范,2.0 版,第 8 章)。配置比特率时,必须根据比特率和采样点计算所有段(和时间量子)的持续时间。对于 CAN FD,名义比特率和数据比特率都是独立执行此操作的。

SocketCAN 相当灵活,可以通过手动设置所有段的持续时间来进行高度自定义配置,或者通过仅设置比特率和采样点(如果未指定,甚至可以按照博世的建议自动选择)来进行便捷配置。但是,每个 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 需要重要的硬件更改,并且从硬件的角度来看并不理想。

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

为 RX 帧添加时间戳

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

处理 TX

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

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

除了优先级轮换外,SW 还必须维护由 TX 缓冲区形成的 FIFO 的头指针和尾指针,以便能够确定哪个缓冲区应在下一个帧中使用 (txb_head) 以及哪个缓冲区应是第一个完成的缓冲区 (txb_tail)。实际的缓冲区索引(显然)模 4(TX 缓冲区的数量),但是指针必须至少宽 1 位,以便能够区分 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] 重新激活。

注意:在开发过程中,讨论了内部硬件流水线是否会中断此清除顺序,以及在清除标志和中断之间是否需要额外的虚拟周期。在 Avalon 接口上,情况确实如此,但 APB 是安全的,因为它使用 2 周期事务。本质上,DOR 标志将被清除,但 DOI 寄存器的 Preset 输入在应用 DOI 清除请求(通过将寄存器的 Reset 输入设置为高电平)的周期内仍然很高。由于 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 指针

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,当读取第一个帧字但

系统暂时没有空闲 SKB,并留下代码稍后解决 SKB 分配时为 0,在 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)

用于接收数据包的轮询例程 (NAPI)

参数

struct napi_struct *napi

NAPI 结构指针

int quota

要处理的最大接收数据包数。

描述

这是 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。

返回

如果 CAN 设备处于睡眠模式,则为 IRQ_NONE,否则为 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 通信控制器模型

说明