前言
- 开组会,感觉全部门的人都会网络,看以太网和 WIFI 体系又过于庞大,看的头昏脑涨。
- 既然那些东西太难了,那就曲线救国,看 CAN 驱动。在 Linux 中 CAN 也是属于网络驱动一部分。
- 但是 CAN 的硬件设计非常的优秀,直接把硬件层和数据链路层的活都搞好了,而应用层又不是驱动工程师该做的事情,因此个人感觉 CAN 驱动还是相对简单的。(叠甲:刚接触,小白的莫名其妙自信)
- 对于 I2C、SPI 这类驱动程序,是被分为了适配器驱动层和设备驱动层,因为这两种协议通常用于主从模式的板级通信,针对具体设备(如传感器、存储器、转换器等)的通信。而每个设备可能有不同的初始化、读写和中断处理需求。因此,设计有设备驱动层,用于处理与每个设备特有的通信协议。
- 而对于 CAN 这类设备,是不会区分主从设备的(可以软件实现,例如 CANopen),他更多的是倾向于传输原始的数据流,因此不进行设备驱动层设计。
- 这个时候有人又要说了,UART 不也是倾向于传输原始数据吗?为什么 UART 适配器驱动层上面还有一个 TTY 和字符设备层呢?这是因为,UART 是传输原始数据没错,但是 UART 多用于控制台。而控制台可以是串口,也可以是 LCD 或键盘等多种形式,为了统一标准输入和标准输出,弄了一个 TTY抽象层。
- 综上所述,CAN 的设计是相对简单的,基本是原厂适配好 CAN 控制器,那么就可以直接移交给应用层进行数据交互了。
- 个人邮箱:zhangyixu02@gmail.com
- 微信公众号:风正豪

正文
Linux 网络通讯设计
- 在 Linux 中,所有的网络通讯采用统一的接口,对于底层实现,数据包发送都是调用的
net_device_ops.ndo_start_xmit
函数。 - 在网络通讯中,所有的数据接收都是采用中断实现(后面又一个 NAPI 机制,即中断 + 轮询,但 CAN 似乎没有采用该机制)。

CAN control 层
- 已提供核心代码,各位根据我提供的代码,与
driver/net/can
目录自行学习,不做赘述。
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/can.h>
#include <linux/can/dev.h>
#include <linux/interrupt.h> #define DRV_NAME "simple_can"struct simple_can_priv {struct can_priv can; int custom_data;
};static const struct can_bittiming_const simple_bittiming_const = {.name = DRV_NAME, .tseg1_min = 4, .tseg1_max = 16, .tseg2_min = 2, .tseg2_max = 8, .sjw_max = 4, .brp_min = 1, .brp_max = 256, .brp_inc = 1,
};
static struct net_device *simple_can_dev;
static int simple_can_open(struct net_device *dev)
{struct simple_can_priv *priv = netdev_priv(dev);open_candev(dev);can_led_event(dev, CAN_LED_EVENT_OPEN);netif_start_queue(dev);printk(KERN_INFO "simple_can: device opened\n");return 0;
}
static int simple_can_close(struct net_device *dev)
{struct simple_can_priv *priv = netdev_priv(dev);netif_stop_queue(dev);close_candev(dev);can_led_event(dev, CAN_LED_EVENT_STOP);printk(KERN_INFO "simple_can: device closed\n");return 0;
}static netdev_tx_t simple_can_start_xmit(struct sk_buff *skb, struct net_device *dev)
{struct simple_can_priv *priv = netdev_priv(dev);struct can_frame *cf = (struct can_frame *)skb->data;if (can_dropped_invalid_skb(ndev, skb))return NETDEV_TX_OK;netif_stop_queue(ndev);can_put_echo_skb(skb, dev, 0);printk(KERN_INFO "simple_can: start xmit\n");return NETDEV_TX_OK;
}static const struct net_device_ops simple_can_netdev_ops = {.ndo_open = simple_can_open, .ndo_stop = simple_can_close, .ndo_start_xmit = simple_can_start_xmit,
};static irqreturn_t simple_can_interrupt(int irq, void *dev_id)
{struct net_device *dev = (struct net_device *)dev_id;struct simple_can_priv *priv = netdev_priv(dev);struct net_device_stats *stats = &ndev->stats;struct can_frame *cf;struct sk_buff *skb;if () {skb = alloc_can_skb(dev, &cf);cf->can_id = ;cf->can_dlc = get_can_dlc();memcpy(cf->data, , cf->can_dlc);stats->rx_packets++; stats->rx_bytes += cf->can_dlc; netif_rx(skb);printk(KERN_INFO "simple_can: received a packet\n");}return IRQ_HANDLED;
}static int simplecan_chip_start(struct net_device *dev)
{return 0;
}static int simplecan_set_mode(struct net_device *dev, enum can_mode mode)
{int err;switch (mode) {case CAN_MODE_START:err = simplecan_chip_start(dev);if (err)return err;netif_wake_queue(dev);break;default:return -EOPNOTSUPP;}return 0;
}static int simplecan_get_berr_counter(const struct net_device *dev,struct can_berr_counter *bec)
{bec->txerr = 0; bec->rxerr = 0; return 0;
}static int __init simple_can_init(void)
{int irq;struct simple_can_priv *priv;irq = platform_get_irq(pdev, 0);simple_can_dev = alloc_candev(0, 0);simple_can_dev->netdev_ops = &simple_can_netdev_ops;simple_can_dev->irq = irq;simple_can_dev->flags |= IFF_ECHO;priv = netdev_priv(simple_can_dev); memset(priv, 0, sizeof(struct simple_can_priv));priv->custom_data = -1; priv->can.clock.freq = 8000000; priv->can.bittiming_const = &simple_bittiming_const; priv->can.do_set_mode = simplecan_set_mode; priv->can.do_get_berr_counter = simplecan_get_berr_counter; priv->can.ctrlmode_supported = CAN_CTRLMODE_BERR_REPORTING |CAN_CTRLMODE_LISTENONLY |CAN_CTRLMODE_LOOPBACK |CAN_CTRLMODE_3_SAMPLES;devm_request_irq(&pdev->dev, simple_can_dev->irq, simple_can_interrupt,0, DRV_NAME, simple_can_dev);register_candev(simple_can_dev);printk(KERN_INFO "simple_can: module loaded\n");return 0;
}static void __exit simple_can_exit(void) {unregister_candev(simple_can_dev);free_candev(simple_can_dev);printk(KERN_INFO "simple_can: module unloaded\n");
}module_init(simple_can_init);
module_exit(simple_can_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("https://zyxbeyourself.blog.csdn.net/");
MODULE_DESCRIPTION("Simple CAN Driver Example");
CAN 驱动测试
-
- 测试 CAN 接口是否正常
ifconfig -a
ip link set can0 down
ip link set can0 type can bitrate 1000000 dbitrate 3000000 fd on
ip -details link show can0
ip link set can0 up
cansend can0 123
cansend can0 00000123
candump can0