设计说明
CAN 模块的源码位于linux-5.10/drivers/net/can/artc_can.c
较早的 linux 内核版本的 CAN 驱动都是基于字符设备驱动实现,提供的功能相对较少,数据包的排队和更高级别的传输协议必须在用户空间的应用程序实现。现在的内核版本普遍采用 socketCAN 实现 CAN 模块的驱动。socketCAN 是将 CAN 作为一种网络设备,基于 linux 内核的网络层实现的软件框架。
CAN 分层结构
CAN 结点的实现可以分为四层结构,各层的主要功能和作用如下图所示:
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 模块的初始化流程如下:
-
释放 reset 和 clock 信号
-
调用 candev,给 device 类型的变量分配空间
-
初始化结构体 priv 的各个成员变量
-
调用 candev 注册 CAN 设备
-
利用内核中网络的 NAPI 机制,定义一个轮询函数,将该函数加入 NAPI 链表,接收中断触发时,由 schedule 调度轮询函数的执行,以轮询方式接收所有的数据包直到接收结束。
-
不采用 NAPI 机制,而是直接在中断中进行轮询,达到最大轮询次数后退出中断。
-
进入中断处理函数后,判断有中断标志置位且轮询次数不超过 20,进入 while 循环。
-
若为发送结束中断,则 stats 的 tx_bytes 和 packets 增加,记录发送的数据字节大小和包个数
-
若为接收中断,则循环接收数据,直到 RXFIFO 为空
-
若出现仲裁错误、总线错误、主动错误、被动错误、数据溢出。则需要进行错误处理,并上报错误类型。
-
清空中断标志
由于 CAN 每帧的数据量只有 8byte,所以发送数据时没有采用 DMA 或中断方式,而是直接调用发送函数将数据发送出去。CAN 模块驱动的数据发送流程如下:
数据结构设计
struct artc_priv {
struct can_priv can; //CAN 公共私有数据结构体
void __iomem *base; //CAN 控制器寄存器基地址
struct clk *clk;
struct reset_control *rst;
};
接口设计
函数原型 | static int artc_can_set_bittiming(struct net_device *dev) |
---|---|
功能说明 | 设置 CAN 模块的位时序 |
参数定义 | dev:指向网络设备的指针 |
返回值 | 0:执行成功 |
注意事项 | - |
函数原型 | static int artc_can_get_berr_counter(const struct net_device *dev, struct can_berr_counter *bec) |
---|---|
功能说明 | 获取 CAN 模块发送、接收错误计数值 |
参数定义 | dev:指向网络设备的指针 bec:获取的发送/接收错误计数值存储到该指针所指向的结构体 |
返回值 | 0:执行成功 |
注意事项 | - |
函数原型 | static netdev_tx_t artc_can_start_xmit(struct sk_buff *skb, struct net_device *dev) |
---|---|
功能说明 | CAN 设备帧发送函数 |
参数定义 | skb:指向套接字缓冲区 dev:指向网络设备的指针 |
返回值 | 0:执行成功 |
注意事项 | - |
函数原型 | static int artc_can_open(struct net_device *dev) |
---|---|
功能说明 | 打开 CAN 网络设备 |
参数定义 | dev:指向网络设备的指针 |
返回值 | 0:执行成功<0:执行失败 |
注意事项 | - |
函数原型 | static int artc_can_close(struct net_device *dev) |
---|---|
功能说明 | 关闭 CAN 网络设备 |
参数定义 | dev:指向网络设备的指针 |
返回值 | 0:执行成功 |
注意事项 | - |
函数原型 | static void artc_can_rx(struct net_device *dev) |
---|---|
功能说明 | CAN 设备的接收函数。该函数在中断中被调用,读出 BUF 中的数据并组合成帧,将帧存储到 buff 结构体中 |
参数定义 | dev:指向网络设备的指针 |
返回值 | 无 |
注意事项 | - |
函数原型 | static int artc_can_err(struct net_device *dev, u8 isrc, u8 status) |
---|---|
参数定义 | dev:指向网络设备的指针 isrc:中断标志位 status:中断状态位 |
返回值 | 0:执行成功<0:执行失败 |
注意事项 | - |