Edit online

设计说明

2 Dec 2024
Read time: 3 minute(s)

CAN 模块的源码位于linux-5.10/drivers/net/can/artc_can.c

较早的 linux 内核版本的 CAN 驱动都是基于字符设备驱动实现,提供的功能相对较少,数据包的排队和更高级别的传输协议必须在用户空间的应用程序实现。现在的内核版本普遍采用 socketCAN 实现 CAN 模块的驱动。socketCAN 是将 CAN 作为一种网络设备,基于 linux 内核的网络层实现的软件框架。

CAN 分层结构

CAN 结点的实现可以分为四层结构,各层的主要功能和作用如下图所示:


can_arch1

CAN 总线的对象层和传输层包括所有由 ISO/OSI 模型定义的数据链路层的服务和功能,相当于是对数据链路层功能的细分。因此,也可以将对象层和传输层看作是 CAN 的数据链路层。

socketCAN 驱动框架

依据 CAN 的分层模型,socketCAN 实现了具体的软件驱动框架,如下图所示(iprouter2/can-utils 只是应用层的一个示例,并不属于 socketCAN 的内容)。

OSI 七层网络模型 CAN 模型 socketCAN 框架实现
应用层 应用层 iprouter2/can-utils
表示层
会话层
传输层
网络层
数据链路层 对象层 CAN core/CAN driver
传输层 CAN 控制器
物理层 物理层 CAN 收发器

CAN core 主要是实现了 CAN 的 socket 配置,并向 driver 提供一些调用的接口,该部分内容由 socketCAN 框架负责。传输层和物理层所对应的 CAN 控制器和收发器皆由硬件实现,所以驱动开发的主要工作是实现 driver 部分的代码。

CAN 的底层驱动实现主要包括以下几部分:

  • 设置 CAN 的位时序

  • 获取 CAN 的发送/接收计数

  • CAN 设备的打开/关闭

  • CAN 设备发送/接收帧信息的操作

  • CAN 设备错误处理的操作

关键流程设计

初始化流程

CAN 模块的初始化流程如下:

  1. 释放 reset 和 clock 信号

  2. 调用 candev,给 device 类型的变量分配空间

  3. 初始化结构体 priv 的各个成员变量

  4. 调用 candev 注册 CAN 设备

中断处理流程
CAN 的中断处理流程由两种方式:
  1. 利用内核中网络的 NAPI 机制,定义一个轮询函数,将该函数加入 NAPI 链表,接收中断触发时,由 schedule 调度轮询函数的执行,以轮询方式接收所有的数据包直到接收结束。

  2. 不采用 NAPI 机制,而是直接在中断中进行轮询,达到最大轮询次数后退出中断。

内核官方文档推荐参考的 MSCAN 和 SJA1000 的驱动中,分别采用了上述的两种方式实现。AIC 的 CAN 模块驱动中断处理流程是采用的第二种方式,即在中断中进行限定次数的轮询。驱动中设置的最大轮询次数为 20。中断处理流程如下:
  1. 进入中断处理函数后,判断有中断标志置位且轮询次数不超过 20,进入 while 循环。

  2. 若为发送结束中断,则 stats 的 tx_bytes 和 packets 增加,记录发送的数据字节大小和包个数

  3. 若为接收中断,则循环接收数据,直到 RXFIFO 为空

  4. 若出现仲裁错误、总线错误、主动错误、被动错误、数据溢出。则需要进行错误处理,并上报错误类型。

  5. 清空中断标志

数据发送流程

由于 CAN 每帧的数据量只有 8byte,所以发送数据时没有采用 DMA 或中断方式,而是直接调用发送函数将数据发送出去。CAN 模块驱动的数据发送流程如下:


start_xmit1

数据结构设计

struct artc_priv {
    struct can_priv can;         //CAN 公共私有数据结构体
    void __iomem *base;       //CAN 控制器寄存器基地址
    struct clk *clk;
    struct reset_control *rst;
};

接口设计

1. artc_can_set_bittiming
函数原型 static int artc_can_set_bittiming(struct net_device *dev)
功能说明 设置 CAN 模块的位时序
参数定义 dev:指向网络设备的指针
返回值 0:执行成功
注意事项 -
2. artc_can_get_berr_counter
函数原型 static int artc_can_get_berr_counter(const struct net_device *dev, struct can_berr_counter *bec)
功能说明 获取 CAN 模块发送、接收错误计数值
参数定义 dev:指向网络设备的指针

bec:获取的发送/接收错误计数值存储到该指针所指向的结构体

返回值 0:执行成功
注意事项 -
3. artc_can_start_xmit
函数原型 static netdev_tx_t artc_can_start_xmit(struct sk_buff *skb, struct net_device *dev)
功能说明 CAN 设备帧发送函数
参数定义 skb:指向套接字缓冲区

dev:指向网络设备的指针

返回值 0:执行成功
注意事项 -
4. artc_can_open
函数原型 static int artc_can_open(struct net_device *dev)
功能说明 打开 CAN 网络设备
参数定义 dev:指向网络设备的指针
返回值 0:执行成功<0:执行失败
注意事项 -
5. artc_can_close
函数原型 static int artc_can_close(struct net_device *dev)
功能说明 关闭 CAN 网络设备
参数定义 dev:指向网络设备的指针
返回值 0:执行成功
注意事项 -
6. artc_can_rx
函数原型 static void artc_can_rx(struct net_device *dev)
功能说明 CAN 设备的接收函数。该函数在中断中被调用,读出 BUF 中的数据并组合成帧,将帧存储到 buff 结构体中
参数定义 dev:指向网络设备的指针
返回值
注意事项 -
7. artc_can_err
函数原型 static int artc_can_err(struct net_device *dev, u8 isrc, u8 status)
参数定义 dev:指向网络设备的指针 isrc:中断标志位 status:中断状态位
返回值 0:执行成功<0:执行失败
注意事项 -