Appearance
CAN驱动实现
CAN(Controller Area Network)是一种串行通信总线,广泛应用于汽车电子、工业自动化和嵌入式系统领域。Linux内核提供了完整的CAN子系统框架(SocketCAN),使得开发者可以方便地实现CAN设备驱动。本章将详细介绍CAN驱动的核心数据结构、API接口和实现细节。
CAN驱动概述
CAN总线的主要特点:
- 多主架构:支持多个节点同时作为主节点发送数据
- 优先级仲裁:通过标识符实现数据帧的优先级仲裁
- 错误检测与恢复:具备强大的错误检测和自动恢复机制
- 长距离通信:支持长达数公里的通信距离
- 实时性:适用于实时控制系统
- 抗干扰能力强:差分信号传输,抗电磁干扰能力强
Linux CAN子系统架构:
- CAN核心:提供核心功能和API接口
- CAN总线驱动:负责管理物理CAN控制器硬件
- SocketCAN接口:提供类似网络套接字的用户空间接口
- CAN协议驱动:实现各种CAN高层协议(如CANopen、J1939等)
核心数据结构
1. CAN设备结构体
struct net_device是网络设备的通用结构体,CAN设备也使用这个结构体:
c
struct net_device {
char name[IFNAMSIZ]; // 设备名称
unsigned long mem_start; // 设备内存起始地址
unsigned long mem_end; // 设备内存结束地址
int base_addr; // I/O基地址
int irq; // 中断号
unsigned char if_port; // 接口类型
unsigned char dma; // DMA通道
unsigned long state; // 设备状态
struct net_device_stats stats; // 设备统计信息
const struct net_device_ops *netdev_ops; // 网络设备操作函数
const struct ethtool_ops *ethtool_ops; // ethtool操作函数
unsigned int flags; // 设备标志
// ... 其他字段
};2. CAN私有数据结构体
CAN设备驱动通常定义一个私有数据结构体,包含CAN特定的信息:
c
struct can_priv {
struct net_device *dev; // 网络设备指针
struct can_bittiming_const bittiming_const; // 位时序约束
struct can_bittiming bittiming; // 当前位时序
struct can_clock clock; // 时钟信息
struct can_berr_counter bec; // 总线错误计数器
unsigned int echo_skb_max; // 回显skb最大数量
struct sk_buff *echo_skb[CAN_ECHO_SKB_MAX]; // 回显skb数组
int echo_idx; // 回显索引
struct napi_struct napi; // NAPI结构
unsigned int restart_ms; // 自动重启延迟(毫秒)
unsigned int controller_mode; // 控制器模式
unsigned int ctrlmode; // 控制模式
unsigned int can_stats_err_mask; // CAN统计错误掩码
struct work_struct restart_work; // 重启工作队列
struct work_struct tx_work; // 发送工作队列
struct sk_buff_head tx_queue; // 发送队列
struct sk_buff_head rx_queue; // 接收队列
spinlock_t tx_queue_lock; // 发送队列锁
spinlock_t rx_queue_lock; // 接收队列锁
// ... 其他字段
};3. CAN帧结构体
struct can_frame定义了CAN数据帧的格式:
c
struct can_frame {
canid_t can_id; // CAN标识符
#define CAN_EFF_FLAG 0x80000000U // 扩展帧标志
#define CAN_RTR_FLAG 0x40000000U // 远程传输请求标志
#define CAN_ERR_FLAG 0x20000000U // 错误帧标志
#define CAN_SFF_MASK 0x000007FFU // 标准帧ID掩码
#define CAN_EFF_MASK 0x1FFFFFFFU // 扩展帧ID掩码
__u8 can_dlc; // 数据长度码(0-8)
__u8 __pad; // 填充字节
__u8 __res0; // 保留字节
__u8 __res1; // 保留字节
__u8 data[8]; // 数据字段
};4. CAN错误帧结构体
struct can_berr_counter定义了CAN总线错误计数器:
c
struct can_berr_counter {
__u16 txerr; // 发送错误计数器
__u16 rxerr; // 接收错误计数器
};5. CAN位时序结构体
struct can_bittiming定义了CAN总线的位时序:
c
struct can_bittiming {
__u32 bitrate; // 位速率(bits/s)
__u32 sample_point; // 采样点位置(1/1000)
__u32 tq; // 时间量子长度(ns)
__u32 prop_seg; // 传播段长度(tq)
__u32 phase_seg1; // 相位段1长度(tq)
__u32 phase_seg2; // 相位段2长度(tq)
__u32 sjw; // 同步跳转宽度(tq)
__u32 brp; // 波特率预分频器
};6. CAN位时序约束结构体
struct can_bittiming_const定义了CAN控制器支持的位时序约束:
c
struct can_bittiming_const {
char name[16]; // 约束名称
__u32 tseg1_min; // 最小tseg1值
__u32 tseg1_max; // 最大tseg1值
__u32 tseg2_min; // 最小tseg2值
__u32 tseg2_max; // 最大tseg2值
__u32 sjw_max; // 最大sjw值
__u32 brp_min; // 最小brp值
__u32 brp_max; // 最大brp值
__u32 brp_inc; // brp增量
__u32 clock_freq; // 控制器时钟频率(Hz)
};核心API接口
1. CAN设备注册与注销
c
// 注册CAN网络设备
int register_netdev(struct net_device *dev);
// 注销CAN网络设备
void unregister_netdev(struct net_device *dev);
// 分配CAN网络设备
struct net_device *alloc_candev(int sizeof_priv, unsigned int echo_skb_max);
// 释放CAN网络设备
void free_candev(struct net_device *dev);2. CAN帧处理
c
// 发送CAN帧
int can_send(struct sk_buff *skb, int flags);
// 接收CAN帧
struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf);
void can_put_echo_skb(struct sk_buff *skb, struct net_device *dev, int idx);
// 处理CAN错误
void can_error(struct net_device *dev, const struct can_frame *cf);3. CAN位时序配置
c
// 计算CAN位时序
int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt);
// 设置CAN位时序
int can_set_bittiming(struct net_device *dev);
// 自动配置CAN位时序
int can_auto_set_bittiming(struct net_device *dev);4. CAN控制器模式配置
c
// 设置CAN控制器模式
int can_change_mode(struct net_device *dev, unsigned int mode);
// 检查CAN控制器模式支持
int can_check_mode(struct net_device *dev, unsigned int mode);5. CAN中断与NAPI
c
// 启用NAPI
void napi_enable(struct napi_struct *n);
// 禁用NAPI
void napi_disable(struct napi_struct *n);
// 调度NAPI
void napi_schedule(struct napi_struct *n);
// NAPI轮询完成
int napi_complete(struct napi_struct *n);CAN设备驱动实现示例
1. 驱动模块初始化
c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/can.h>
#include <linux/can/dev.h>
#include <linux/can/error.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/delay.h>
#define CAN_DEVICE_NAME "can0"
#define CAN_IRQ_NUM 42 // 假设CAN控制器使用IRQ 42
#define CAN_BASE_ADDR 0x1000 // 假设CAN控制器I/O基地址为0x1000
#define CAN_CLOCK_FREQ 24000000 // CAN控制器时钟频率为24MHz
// CAN控制器寄存器定义
#define CAN_REG_CTRL 0x00 // 控制寄存器
#define CAN_REG_STATUS 0x04 // 状态寄存器
#define CAN_REG_INT 0x08 // 中断寄存器
#define CAN_REG_BTR 0x0C // 波特率寄存器
#define CAN_REG_DATA 0x10 // 数据寄存器
#define CAN_REG_ID 0x14 // 标识符寄存器
// CAN私有数据结构体
struct can_priv_data {
struct net_device *dev; // 网络设备指针
void __iomem *base; // 映射后的I/O基地址
int irq; // 中断号
struct can_bittiming_const bittiming_const; // 位时序约束
struct can_priv can; // CAN私有数据
struct napi_struct napi; // NAPI结构
struct sk_buff_head tx_queue; // 发送队列
spinlock_t tx_queue_lock; // 发送队列锁
};
// CAN设备实例
static struct can_priv_data *can_dev_priv;2. CAN设备操作函数
c
// 打开CAN设备
static int can_open(struct net_device *dev)
{
struct can_priv_data *priv = netdev_priv(dev);
int ret;
// 申请中断
ret = request_irq(priv->irq, can_interrupt, IRQF_SHARED, dev->name, dev);
if (ret < 0) {
netdev_err(dev, "Failed to request IRQ %d\n", priv->irq);
return ret;
}
// 启用CAN控制器
writel(0x01, priv->base + CAN_REG_CTRL);
msleep(10);
// 启用NAPI
napi_enable(&priv->napi);
// 启用中断
writel(0x0F, priv->base + CAN_REG_INT); // 启用接收、发送、错误和状态中断
return 0;
}
// 关闭CAN设备
static int can_stop(struct net_device *dev)
{
struct can_priv_data *priv = netdev_priv(dev);
// 禁用中断
writel(0x00, priv->base + CAN_REG_INT);
// 禁用NAPI
napi_disable(&priv->napi);
// 禁用CAN控制器
writel(0x00, priv->base + CAN_REG_CTRL);
// 释放中断
free_irq(priv->irq, dev);
return 0;
}
// 启动CAN设备队列
static int can_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct can_priv_data *priv = netdev_priv(dev);
struct can_frame *cf = (struct can_frame *)skb->data;
unsigned long flags;
// 检查帧长度
if (skb->len != sizeof(struct can_frame)) {
dev_kfree_skb(skb);
return NETDEV_TX_OK;
}
// 检查CAN控制器是否可以发送
if (!(readl(priv->base + CAN_REG_STATUS) & 0x02)) {
// 发送队列已满,将帧加入发送队列
spin_lock_irqsave(&priv->tx_queue_lock, flags);
if (skb_queue_len(&priv->tx_queue) < 10) {
skb_queue_tail(&priv->tx_queue, skb);
spin_unlock_irqrestore(&priv->tx_queue_lock, flags);
return NETDEV_TX_BUSY;
}
spin_unlock_irqrestore(&priv->tx_queue_lock, flags);
// 发送队列已满,丢弃帧
dev_kfree_skb(skb);
dev->stats.tx_dropped++;
return NETDEV_TX_OK;
}
// 发送CAN帧
can_transmit_frame(priv, cf);
// 保存回显帧
can_put_echo_skb(skb, dev, 0);
// 更新统计信息
dev->stats.tx_packets++;
dev->stats.tx_bytes += cf->can_dlc;
return NETDEV_TX_OK;
}
// CAN设备NAPI轮询函数
static int can_poll(struct napi_struct *napi, int budget)
{
struct can_priv_data *priv = container_of(napi, struct can_priv_data, napi);
struct net_device *dev = priv->dev;
int work_done = 0;
// 处理接收的CAN帧
while (work_done < budget && (readl(priv->base + CAN_REG_STATUS) & 0x01)) {
struct sk_buff *skb = alloc_can_skb(dev, NULL);
if (!skb) {
dev->stats.rx_dropped++;
break;
}
struct can_frame *cf = (struct can_frame *)skb->data;
// 从硬件读取CAN帧
can_receive_frame(priv, cf);
// 设置帧长度
skb->len = sizeof(struct can_frame);
skb->protocol = htons(ETH_P_CAN);
// 提交接收帧
netif_receive_skb(skb);
dev->stats.rx_packets++;
dev->stats.rx_bytes += cf->can_dlc;
work_done++;
}
// 如果没有更多工作要做,退出NAPI
if (work_done < budget) {
napi_complete(napi);
// 重新启用中断
writel(0x0F, priv->base + CAN_REG_INT);
}
return work_done;
}
// 设置CAN网络设备参数
static int can_set_config(struct net_device *dev, struct ifreq *ifr)
{
struct can_priv_data *priv = netdev_priv(dev);
int ret;
// 处理CAN设备配置
ret = can_do_set_bittiming(dev, ifr);
if (ret < 0)
return ret;
ret = can_do_set_bitrate(dev, ifr);
if (ret < 0)
return ret;
ret = can_do_set_mode(dev, ifr);
if (ret < 0)
return ret;
return 0;
}
// 获取CAN网络设备参数
static int can_get_config(struct net_device *dev, struct ifreq *ifr)
{
return can_do_get_bittiming(dev, ifr);
}
// CAN网络设备操作函数结构体
static const struct net_device_ops can_netdev_ops = {
.ndo_open = can_open,
.ndo_stop = can_stop,
.ndo_start_xmit = can_start_xmit,
.ndo_set_config = can_set_config,
.ndo_get_config = can_get_config,
};3. CAN中断处理函数
c
// CAN中断处理函数
static irqreturn_t can_interrupt(int irq, void *dev_id)
{
struct net_device *dev = dev_id;
struct can_priv_data *priv = netdev_priv(dev);
u32 int_status, status;
// 读取中断状态
int_status = readl(priv->base + CAN_REG_INT);
status = readl(priv->base + CAN_REG_STATUS);
// 处理接收中断
if (int_status & 0x01 && (status & 0x01)) {
// 禁用接收中断
writel(0x0E, priv->base + CAN_REG_INT);
// 调度NAPI
napi_schedule(&priv->napi);
}
// 处理发送完成中断
if (int_status & 0x02 && (status & 0x04)) {
struct sk_buff *skb;
unsigned long flags;
// 处理发送队列中的下一个帧
spin_lock_irqsave(&priv->tx_queue_lock, flags);
skb = skb_dequeue(&priv->tx_queue);
spin_unlock_irqrestore(&priv->tx_queue_lock, flags);
if (skb) {
struct can_frame *cf = (struct can_frame *)skb->data;
// 发送CAN帧
can_transmit_frame(priv, cf);
// 保存回显帧
can_put_echo_skb(skb, dev, 0);
// 更新统计信息
dev->stats.tx_packets++;
dev->stats.tx_bytes += cf->can_dlc;
}
}
// 处理错误中断
if (int_status & 0x04) {
// 读取错误状态并处理
u32 error = readl(priv->base + CAN_REG_STATUS);
struct can_frame cf = {
.can_id = CAN_ERR_FLAG | CAN_ERR_CRTL,
.can_dlc = 8,
};
// 设置错误信息
cf.data[0] = error;
can_error(dev, &cf);
}
return IRQ_HANDLED;
}
// 从CAN硬件接收帧
static void can_receive_frame(struct can_priv_data *priv, struct can_frame *cf)
{
u32 id_reg, data_reg;
// 读取标识符寄存器
id_reg = readl(priv->base + CAN_REG_ID);
// 设置CAN帧标识符
if (id_reg & 0x80000000) {
// 扩展帧
cf->can_id = CAN_EFF_FLAG | (id_reg & 0x1FFFFFFF);
} else {
// 标准帧
cf->can_id = id_reg & 0x7FF;
}
// 检查是否为远程帧
if (id_reg & 0x40000000) {
cf->can_id |= CAN_RTR_FLAG;
}
// 读取数据长度
cf->can_dlc = (id_reg >> 16) & 0x0F;
// 读取数据
data_reg = readl(priv->base + CAN_REG_DATA);
cf->data[0] = (data_reg >> 0) & 0xFF;
cf->data[1] = (data_reg >> 8) & 0xFF;
cf->data[2] = (data_reg >> 16) & 0xFF;
cf->data[3] = (data_reg >> 24) & 0xFF;
if (cf->can_dlc > 4) {
data_reg = readl(priv->base + CAN_REG_DATA + 4);
cf->data[4] = (data_reg >> 0) & 0xFF;
cf->data[5] = (data_reg >> 8) & 0xFF;
cf->data[6] = (data_reg >> 16) & 0xFF;
cf->data[7] = (data_reg >> 24) & 0xFF;
}
}
// 向CAN硬件发送帧
static void can_transmit_frame(struct can_priv_data *priv, struct can_frame *cf)
{
u32 id_reg = 0, data_reg = 0;
// 设置标识符
if (cf->can_id & CAN_EFF_FLAG) {
// 扩展帧
id_reg = CAN_EFF_FLAG | (cf->can_id & CAN_EFF_MASK);
} else {
// 标准帧
id_reg = cf->can_id & CAN_SFF_MASK;
}
// 设置远程帧标志
if (cf->can_id & CAN_RTR_FLAG) {
id_reg |= CAN_RTR_FLAG;
}
// 设置数据长度
id_reg |= (cf->can_dlc & 0x0F) << 16;
// 写入标识符寄存器
writel(id_reg, priv->base + CAN_REG_ID);
// 写入数据
data_reg = (cf->data[0] << 0) | (cf->data[1] << 8) |
(cf->data[2] << 16) | (cf->data[3] << 24);
writel(data_reg, priv->base + CAN_REG_DATA);
if (cf->can_dlc > 4) {
data_reg = (cf->data[4] << 0) | (cf->data[5] << 8) |
(cf->data[6] << 16) | (cf->data[7] << 24);
writel(data_reg, priv->base + CAN_REG_DATA + 4);
}
// 启动发送
writel(0x02, priv->base + CAN_REG_CTRL);
}4. CAN设备初始化与清理
c
// 初始化CAN位时序约束
static void can_init_bittiming_const(struct can_priv_data *priv)
{
struct can_bittiming_const *btc = &priv->bittiming_const;
strcpy(btc->name, "can_example");
btc->tseg1_min = 1;
btc->tseg1_max = 16;
btc->tseg2_min = 1;
btc->tseg2_max = 8;
btc->sjw_max = 4;
btc->brp_min = 1;
btc->brp_max = 128;
btc->brp_inc = 1;
btc->clock_freq = CAN_CLOCK_FREQ;
}
// 初始化CAN设备
static struct net_device *can_init_dev(void)
{
struct net_device *dev;
struct can_priv_data *priv;
// 分配CAN网络设备
dev = alloc_candev(sizeof(struct can_priv_data), 1);
if (!dev) {
pr_err("Failed to allocate CAN device\n");
return NULL;
}
priv = netdev_priv(dev);
priv->dev = dev;
priv->irq = CAN_IRQ_NUM;
// 映射I/O基地址
priv->base = ioremap(CAN_BASE_ADDR, 0x20);
if (!priv->base) {
pr_err("Failed to ioremap CAN base address\n");
free_candev(dev);
return NULL;
}
// 初始化CAN位时序约束
can_init_bittiming_const(priv);
// 初始化CAN私有数据
priv->can.bittiming_const = &priv->bittiming_const;
priv->can.clock.freq = CAN_CLOCK_FREQ;
// 设置网络设备操作函数
dev->netdev_ops = &can_netdev_ops;
// 设置设备名称
strcpy(dev->name, CAN_DEVICE_NAME);
// 初始化NAPI
netif_napi_add(dev, &priv->napi, can_poll, 64);
// 初始化发送队列
skb_queue_head_init(&priv->tx_queue);
spin_lock_init(&priv->tx_queue_lock);
return dev;
}
// 清理CAN设备
static void can_cleanup_dev(struct net_device *dev)
{
struct can_priv_data *priv = netdev_priv(dev);
// 释放I/O映射
iounmap(priv->base);
// 释放网络设备
free_candev(dev);
}5. 模块初始化与退出
c
// 模块初始化函数
static int __init can_driver_init(void)
{
int ret;
// 初始化CAN设备
can_dev_priv = netdev_priv(can_init_dev());
if (!can_dev_priv) {
return -ENOMEM;
}
// 注册CAN网络设备
ret = register_netdev(can_dev_priv->dev);
if (ret < 0) {
pr_err("Failed to register CAN device\n");
can_cleanup_dev(can_dev_priv->dev);
return ret;
}
pr_info("CAN driver initialized successfully\n");
return 0;
}
// 模块退出函数
static void __exit can_driver_exit(void)
{
// 注销CAN网络设备
unregister_netdev(can_dev_priv->dev);
// 清理CAN设备
can_cleanup_dev(can_dev_priv->dev);
pr_info("CAN driver exited\n");
}
module_init(can_driver_init);
module_exit(can_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Linux Driver Developer");
MODULE_DESCRIPTION("CAN Example Driver");
MODULE_VERSION("1.0");CAN错误处理
CAN驱动需要处理各种错误情况,包括:
1. 总线错误处理
c
// 处理CAN总线错误
static void can_handle_bus_error(struct can_priv_data *priv)
{
struct net_device *dev = priv->dev;
u32 error_status = readl(priv->base + CAN_REG_STATUS);
// 更新错误计数器
if (error_status & 0x10) {
dev->stats.tx_errors++;
}
if (error_status & 0x20) {
dev->stats.rx_errors++;
}
// 发送错误帧
struct can_frame cf = {
.can_id = CAN_ERR_FLAG | CAN_ERR_PROT,
.can_dlc = 8,
.data = { error_status, 0, 0, 0, 0, 0, 0, 0 }
};
can_error(dev, &cf);
}2. 超时处理
c
// CAN发送超时处理
static void can_tx_timeout(struct net_device *dev)
{
struct can_priv_data *priv = netdev_priv(dev);
// 重置CAN控制器
writel(0x00, priv->base + CAN_REG_CTRL);
msleep(10);
writel(0x01, priv->base + CAN_REG_CTRL);
msleep(10);
// 重新启用中断
writel(0x0F, priv->base + CAN_REG_INT);
// 重置发送队列
spin_lock_bh(&priv->tx_queue_lock);
while (!skb_queue_empty(&priv->tx_queue)) {
struct sk_buff *skb = skb_dequeue(&priv->tx_queue);
dev_kfree_skb(skb);
dev->stats.tx_dropped++;
}
spin_unlock_bh(&priv->tx_queue_lock);
netif_wake_queue(dev);
netdev_warn(dev, "CAN TX timeout occurred\n");
}CAN设备的用户空间接口
通过SocketCAN,用户空间可以使用标准的套接字接口访问CAN设备:
1. 基本CAN套接字操作
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
int main(void)
{
int s;
struct sockaddr_can addr;
struct ifreq ifr;
struct can_frame frame;
// 创建CAN套接字
s = socket(PF_CAN, SOCK_RAW, CAN_RAW);
if (s < 0) {
perror("socket");
return 1;
}
// 指定CAN接口
strcpy(ifr.ifr_name, "can0");
ioctl(s, SIOCGIFINDEX, &ifr);
// 绑定CAN接口
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
bind(s, (struct sockaddr *)&addr, sizeof(addr));
// 发送CAN帧
frame.can_id = 0x123;
frame.can_dlc = 2;
frame.data[0] = 0x11;
frame.data[1] = 0x22;
write(s, &frame, sizeof(frame));
// 接收CAN帧
read(s, &frame, sizeof(frame));
printf("Received CAN frame: ID=0x%X, DLC=%d, Data=0x%02X 0x%02X\n",
frame.can_id, frame.can_dlc, frame.data[0], frame.data[1]);
close(s);
return 0;
}2. CAN过滤器配置
c
// 配置CAN过滤器
struct can_filter rfilter;
rfilter.can_id = 0x123;
rfilter.can_mask = CAN_SFF_MASK; // 只接收ID为0x123的标准帧
// 设置过滤器
int nfilters = 1;
if (setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter)) < 0) {
perror("setsockopt");
return 1;
}高级CAN功能
1. CAN FD支持
CAN FD(Flexible Data-Rate)是CAN总线的扩展,支持更高的数据传输速率和更长的数据帧。Linux内核从3.6版本开始支持CAN FD:
c
// 启用CAN FD模式
int enable_fd = 1;
if (setsockopt(s, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &enable_fd, sizeof(enable_fd)) < 0) {
perror("setsockopt CAN_RAW_FD_FRAMES");
return 1;
}
// 发送CAN FD帧
struct canfd_frame frame;
frame.can_id = 0x123;
frame.len = 16; // CAN FD支持最多64字节数据
frame.data[0] = 0x11;
// ... 设置其他数据字节
write(s, &frame, sizeof(frame));2. CAN错误帧监控
c
// 启用错误帧监控
int enable_err = 1;
if (setsockopt(s, SOL_CAN_RAW, CAN_RAW_ERR_FILTER, &enable_err, sizeof(enable_err)) < 0) {
perror("setsockopt CAN_RAW_ERR_FILTER");
return 1;
}
// 接收错误帧
struct can_frame err_frame;
read(s, &err_frame, sizeof(err_frame));
if (err_frame.can_id & CAN_ERR_FLAG) {
printf("CAN error occurred: 0x%X\n", err_frame.can_id);
}最佳实践
- 使用NAPI:对于高速CAN控制器,使用NAPI可以提高中断处理效率
- 错误检测与恢复:实现完善的错误检测和自动恢复机制
- 位时序配置:提供灵活的位时序配置选项,支持多种波特率
- 资源管理:正确管理内存、中断、DMA等资源
- 性能优化:
- 使用环形缓冲区提高数据传输效率
- 批量处理接收和发送的CAN帧
- 减少中断延迟
- 调试信息:
- 使用dev_dbg()提供详细的调试信息
- 实现sysfs接口用于运行时配置和状态查询
- 标准兼容性:
- 遵循SocketCAN接口标准
- 支持CAN FD等扩展功能
- 实现标准的网络设备操作函数
总结
CAN驱动实现主要包括以下步骤:
- 定义CAN私有数据结构体和网络设备操作函数
- 实现CAN设备的打开、关闭和数据传输功能
- 实现中断处理和NAPI轮询机制
- 处理CAN帧的接收和发送
- 实现错误处理和恢复机制
- 配置CAN位时序和控制器参数
- 注册和注销CAN网络设备
通过Linux CAN子系统提供的API接口,开发者可以方便地实现各种CAN设备驱动,支持从低速传感器到高速控制单元等多种CAN设备。SocketCAN接口使得用户空间程序可以像使用网络套接字一样方便地访问CAN设备,大大简化了CAN应用程序的开发。