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_reg 和 write_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 中处理,即
TX 完成。当设备成功完成帧的传输时,该帧会在本地回显。如果发生错误,则会向网络子系统发送一个信息错误帧 [2]。在这两种情况下,都会恢复软件 TX 队列,以便可以发送更多帧。
错误条件。如果出现问题(例如,设备进入总线关闭状态或发生 RX 溢出),则会更新错误计数器,并将信息错误帧排队到 SW RX 队列。
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 中,持续时间寄存器 PROP
和 PH1
具有不同的宽度(分别为 6 位和 7 位),因此自动计算的值可能会溢出较短的寄存器,因此必须在两个之间重新分配 [4]。
处理 RX¶
帧接收在 NAPI 队列中处理,当设置 RXNE(RX FIFO 不为空)位时,从 ISR 启用该队列。逐个读取帧,直到 RX FIFO 中没有剩余帧或达到 NAPI 轮询运行的最大工作配额(请参阅)。然后将每个帧传递到网络接口 RX 队列。
传入帧可以是 CAN 2.0 帧或 CAN FD 帧。在内核中区分这两者的方法是分配 struct can_frame
或 struct canfd_frame
,两者具有不同的大小。在控制器中,有关帧类型的信息存储在 RX FIFO 的第一个字中。
这给我们带来了一个先有鸡还是先有蛋的问题:我们想要为帧分配 skb
,只有在成功的情况下才从 FIFO 获取帧;否则将其保留在那里以供以后使用。但是为了能够分配正确的 skb
,我们必须获取 FIFO 的第一个字。有几种可能的解决方案
读取该字,然后分配。如果失败,则丢弃帧的其余部分。当系统内存不足时,情况无论如何都很糟糕。
始终预先分配足够大的
skb
以用于 FD 帧。然后调整skb
的内部结构,使其看起来像是为较小的 CAN 2.0 帧分配的。添加选项以窥视 FIFO 而不是消耗该字。
如果分配失败,则将读取的字存储到驱动程序的数据中。在下一次尝试时,使用存储的字而不是再次读取它。
选项 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 为空——在这种情况下,。如何在维护 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 |
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 - 由于以下原因之一,帧未插入
TXT 缓冲区不可写(处于错误状态)
无效的 TXT 缓冲区索引
无效的帧长度
-
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 核和驱动程序开发感谢¶
Odrej Ille <ondrej.ille@gmail.com>
作为捷克技术大学 FEE 测量系的*学生启动了该项目
多年来为该项目投入了大量的个人时间和热情
从事更多有资金的任务
-
是多年来该项目的主要投资者
在其用于 斯柯达汽车的 CAN/CAN FD 诊断框架中使用该项目
-
资助项目 CAN FD 开放核心支持基于 Linux 内核的系统
与捷克技术大学谈判并付款,以允许公众访问该项目
为这项工作提供了额外的资金
-
解决项目 CAN FD 开放核心支持基于 Linux 内核的系统
提供 GitLab 管理
用于持续集成的虚拟服务器和计算能力
为 HIL 持续集成测试提供硬件
-
启动项目开源准备工作的小额资金
Petr Porazil <porazil@pikron.com>
PCIe 收发器附加板的设计和板的组装
基于 MicroZed/Zynq 系统的 MZ_APO 底板的设计和组装
Martin Jerabek <martin.jerabek01@gmail.com>
Linux 驱动程序开发
持续集成平台架构师和 GHDL 更新
Jiri Novak <jnovak@fel.cvut.cz>
在捷克技术大学 FEE 测量系的项目启动、管理和使用
Pavel Pisa <pisa@cmp.felk.cvut.cz>
在捷克技术大学 FEE 控制工程系启动开源、项目协调和管理
Jaroslav Beran<jara.beran@gmail.com>
Intel SoC 的系统集成、核心和驱动程序测试和更新
Carsten Emde (OSADL)
提供了 OSADL 专业知识来讨论 IP 核许可
指出了 LGPL 和 CAN 总线可能的专利案例可能出现的死锁,这导致将 IP 核设计重新许可为类似 BSD 的许可证
Reiner Zitzmann 和 Holger Zeltwanger (CAN in Automation)
提供了建议和帮助,以告知社区有关该项目的信息,并邀请我们参加专注于 CAN 总线未来发展方向的活动
Jan Charvat
为 QEMU 实现了 CTU CAN FD 功能模型,该模型已集成到 QEMU 主线中 (docs/system/devices/can.rst)
学士论文 QEMU 仿真器的 CAN FD 通信控制器模型