7.1.5. 设计说明

7.1.5.1. 源码说明

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

7.1.5.2. 模块架构

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

7.1.5.2.1. CAN分层结构

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

../../../_images/can_arch.png

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

7.1.5.2.2. socketCAN驱动框架

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

OSI七层网络模型

CAN模型

socketCAN框架实现

应用层

应用层

iprouter2/can-utils

表示层

会话层

传输层

网络层

数据链路层

对象层

CAN core/CAN driver

传输层

CAN控制器

物理层

物理层

CAN收发器

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

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

  • 设置CAN的位时序

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

  • CAN设备的打开/关闭

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

  • CAN设备错误处理的操作

7.1.5.3. 关键流程设计

7.1.5.3.1. 初始化流程

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

  1. 释放reset和clock信号

  2. 调用alloc_candev,给struct net_device类型的变量分配空间

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

  4. 调用register_candev注册CAN设备

7.1.5.3.2. 中断处理流程

CAN的中断处理流程由两种方式:

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

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

内核官方文档推荐参考的MSCAN和SJA1000的驱动中,分别采用了上述的两种方式实现。AIC的CAN模块驱动中断处理流程是采用的第二种方式,即在中断中进行限定次数的轮询。驱动中设置的最大轮询次数为20。中断处理流程如下:

  1. 进入中断处理函数后,判断有中断标志置位且轮询次数不超过20,进入while循环。

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

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

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

  5. 清空中断标志

7.1.5.3.3. 数据发送流程

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

../../../_images/start_xmit.png

7.1.5.4. 数据结构设计

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

7.1.5.5. 接口设计

7.1.5.5.1. artc_can_set_bittiming

函数原型

static int artc_can_set_bittiming(struct net_device *dev)

功能说明

设置CAN模块的位时序

参数定义

dev:指向网络设备的指针

返回值

0:执行成功

注意事项

7.1.5.5.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:执行成功

注意事项

7.1.5.5.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:执行成功

注意事项

7.1.5.5.4. artc_can_open

函数原型

static int artc_can_open(struct net_device *dev)

功能说明

打开CAN网络设备

参数定义

dev:指向网络设备的指针

返回值

0:执行成功
<0:执行失败

注意事项

7.1.5.5.5. artc_can_close

函数原型

static int artc_can_close(struct net_device *dev)

功能说明

关闭CAN网络设备

参数定义

dev:指向网络设备的指针

返回值

0:执行成功

注意事项

7.1.5.5.6. artc_can_rx

函数原型

static void artc_can_rx(struct net_device *dev)

功能说明

CAN设备的接收函数。该函数在中断中被调用,读出CAN BUF中的数据并组合成帧,将帧存储到sk_buff结构体中

参数定义

dev:指向网络设备的指针

返回值

注意事项

7.1.5.5.7. artc_can_err

函数原型

static int artc_can_err(struct net_device *dev, u8 isrc, u8 status)

参数定义

dev:指向网络设备的指针
isrc:中断标志位
status:中断状态位

返回值

0:执行成功
<0:执行失败

注意事项