前言
开组会,感觉全部门的人都会网络,看以太网和 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