Linux内核Socket CAN中文文档.docx
- 文档编号:28270020
- 上传时间:2023-07-10
- 格式:DOCX
- 页数:17
- 大小:27.15KB
Linux内核Socket CAN中文文档.docx
《Linux内核Socket CAN中文文档.docx》由会员分享,可在线阅读,更多相关《Linux内核Socket CAN中文文档.docx(17页珍藏版)》请在冰豆网上搜索。
Linux内核SocketCAN中文文档
Linux内核SocketCAN中文文档
Linux内核SocketCAN中文文档分类:
嵌入式内核驱动2012-06-0820:
363711人阅读评论
(1)收藏举报socketlinux内核网络structfilterlinux自己在年假中空闲之余翻译的内核中SocketCAN的文档,原文地址在:
http:
//lxr.linux.no/linux+v2.6.34/Documentation/networking/can.txt但是这篇文档没有涉及广播管理协议套接字(SOCK_DGRAM)的内容。
另外一篇比较好的SocketCAN的英文文档是(详细介绍了广播管理协议套接字):
LowLevelCANFrameworkApplicationProgrammersInterfacehttp:
//www.brownhat.org/docs/socketcan/llcf-api.html#SECTION00051000000000000000自己暂时没时间翻译这篇文章了,有空再说吧。
自己英文水平有限,希望高人指正。
================================================================================这篇文章主要针对can协议簇(akasocketcan)这篇文章包含以下内容:
===============1概述--什么是SocketCAN?
2动机--为什么使用socketAPI接口?
3SocketCAN详解3.1接收队列3.2发送帧的本地回环3.3网络安全相关3.4网络故障监测
4如何使用SocketCAN4.1使用can_filter的原始套接字(RAWsocket)4.1.1原始套接字选项CAN_RAW_FILTER4.1.2原始套接字选项CAN_RAW_ERR_FILTER4.1.3原始套接字选项CAN_RAW_LOOPBACK4.1.4原始套接字选项CAN_RAW_RECV_OWN_MSGS4.2广播管理协议套接字(SOCK_DGRAM)4.3面向连接的传输协议(SOCK_SEQPACKET)4.4无连接的传输协议(SOCK_DGRAM)
5SocketCAN核心模块5.1can.ko模块的参数5.2procfs接口5.3写一个自己的CAN协议模块
6CAN网络驱动6.1常见设置6.2发送帧的本地回环6.3CAN控制器的硬件过滤6.4虚拟的CAN驱动(vcan)6.5CAN网络设备驱动接口6.5.1Netlink接口--设置/获取设备属性6.5.2设置CAN的比特_时序6.5.3启动和停止CAN网络设备6.6支持SocketCAN的硬件
7学习SocketCAN的相关资源
8贡献者名单
==============现在开始===================1.概述--什么是SocketCAN?
==================
socketcan子系统是在Linux下CAN协议(ControllerAreaNetwork)实现的一种实现方法。
CAN是一种在世界范围内广泛用于自动控制、嵌入式设备和汽车领域的网络技术。
Linux下最早使用CAN的方法是基于字符设备来实现的,与之不同的是SocketCAN使用伯克利的socket接口和linux网络协议栈,这种方法使得can设备驱动可以通过网络接口来调用。
SocketCAN的接口被设计的尽量接近TCP/IP的协议,让那些熟悉网络编程的程序员能够比较容易的学习和使用。
2.动机--为什么使用socketAPI接口?
=======================
在SocketCAN之前Linux中已经有了一些CAN的实现方法,那为什么还要启动SocketCAN这个项目呢?
大多数已经存在的实现方法仅仅作为某个具体硬件的设备驱动,它们往往基于字符设备并且提供的功能很少。
那些方案通常是由一个针对具体硬件的设备驱动提供的字符设备接口来实现原始can帧的发送和接收,并且直接和控制器硬件打交道。
帧队列和ISO-TP这样的高层协议必须在用户空间来实现。
就像串口设备接口一样,大多数基于字符设备的实现在同一时刻仅仅支持一个进程的访问。
如果更换了CAN控制器,那么同时也要更换另一个设备驱动,并且需要大多数应用程序重新调整以适应新驱动的API。
SocketCAN被设计用来克服以上种种不足。
这种新的协议族实现了用户空间的socket接口,它构建于Linux网络层之上,因此可以直接使用已有的队列功能。
CAN控制器的设备驱动将自己作为一个网络设备注册进Linux的网络层,CAN控制器收到的CAN帧可以传输给高层的网络协议和CAN协议族,反之,发送的帧也会通过高层给CAN控制器。
传输协议模块可以使用协议族提供的接口注册自己,所以可以动态的加载和卸载多个传输协议。
事实上,CAN核心模块不提供任何协议,也不能在没有加载其它协议的情况下单独使用。
同一时间可以在相同或者不同的协议上打开多个套接字,可以在相同或者不同的CANID上同时监听和发送(listen/send)。
几个同时监听具有相同ID帧的套接字可以在匹配的帧到来后接收到相同的内容。
如果一个应用程序希望使用一个特殊的协议(比如ISO-TP)进行通信,只要在打开套接字的时候选择那个协议就可以了,接下来就可以读取和写入应用数据流了,根本无需关心CAN-ID和帧的结构等信息。
使用字符设备也可以让用户空间获得类似的便利,但是这中解决方案在技术上不够优雅,原因如下:
*复杂的操作。
使用SocketCAN,只需要向socket
(2)函数传递协议参数并使用bind
(2)选择CAN接口和CANID,基于字符设备实现这样的功能却要使用ioctl
(2)来完成所有的操作。
*无法代码复用。
字符设备无法使用Linux的网络队列代码,所以必须为CAN网络重写这部分功能。
*缺乏抽象性。
在大多数已经实现的字符设备方案中,硬件相关的CAN控制器设备驱动直接提供应用程序需要的字符设备。
在Unix系统中,无论对于字符设备还是块设备,这都是不常见的。
比如,你不会为某个串口、电脑上的某个声卡、访问磁带和硬盘的SCSI/IDE控制器直接创建一个字符设备。
相反,你会将向应用程序提供统一字符设备/块设备接口的功能交给一个抽象层来做,你自己仅仅提供硬件相关的设备驱动接口。
这种抽象是通过子系统来完成的,比如tty层子系统、声卡子系统和SCSI/IDE子系统。
实现CAN设备驱动最简单的办法就是直接提供一个字符设备而不使用(或不完全使用)抽象层,大多数已经存在的驱动也是这么做的。
但是正确的方法却要增加抽象层以支持各种功能,如注册一个特定的CAN-ID,支持多次打开的操作和这些操作之间的CAN帧复用,支持CAN帧复杂的队列功能,还要提供注册设备驱动的API。
然而使用Linux内核提供的网络框架将会大大简化,这就是SocketCAN要做的。
在Linux中实现CAN功能最自然和合适的方式就是使用内核的网络框架。
3.SocketCAN详解============
就像第二章所讲的那样,使用SocketCAN的主要目的就是为用户空间的应用程序提供基于Linux网络层的套接字接口。
与广为人知的TCP/IP协议以及以太网不同,CAN总线没有类似以太网的MAC层地址,只能用于广播。
CANID仅仅用来进行总线的仲裁。
因此CANID在总线上必须是唯一的。
当设计一个CAN-ECU(ElectronicControlUnit电子控制单元)网络的时候,CAN-ID可以映射到具体的ECU。
因此CAN-ID可以当作发送源的地址来使用。
3.1接收队列---------------
允许多个应用程序同时访问网络导致了新的问题出现,那就是不同的应用程序可能会在同一个CAN网络接口上对具有相同CAN-ID的帧感兴趣。
SocketCAN的核心部分实现了SocketCAN的协议族,通过高效的接收队列解决了这个问题。
比如一个用户空间的程序打开了一个原始CAN套接字,原始协议模块将向CAN套接字的核心模块申请用户空间需要的一系列CAN-ID。
SocketCAN的核心向CAN协议模块提供预约和解约CAN-ID的接口--can_rx_(un)register(),无论这个CAN-ID是针对一个具体的CAN接口还是所有已知的CAN接口(参考第5章)。
为了优化CPU的运行效率,每个设备都对应一个接收队列,这样比较容易实现各种报文过滤规则。
3.2发送帧的本地回环
-----------------在其它种类的网络中,在相同或者不同网络节点上的应用程序都可以相互交换数据。
___________________|_||_||_||__||_|||A||||B||||C||||A||B||||C|||___||___||___||_______||___||||||-----------------
(1)-CANbus-
(2)---------------请看上图的两个例子。
为了保证应用程序A在两个例子中能够接收到同样的例子(例2中A和B在同一个CAN设备上),发送出去的CAN帧需要能够在本地回环。
Linux下的网络设备仅仅处理物理媒介上帧的发送和接受。
总线仲裁机制下,高优先级的帧会将低优先级的帧延后。
为了正确反映实际的通信活动,回环必须在正确传输成功之后进行。
如果CAN网络的硬件不支持回环功能,一种低效的方案是使用SocketCAN核心部分来实现软件回环。
具体的情况请参考6.2小节。
CAN网络的回环功能是默认开启的。
由于RT-SocketCAN的特殊需求,每个套接字的回环功能可以被独立关闭。
CAN原始套接字的控制选项请参考4.1小节。
*当你在同一个节点上运行CAN分析命令“candump”或者“cansniffer”的时候就会发现回环功能真的很有用。
3.3网络安全相关
--------------------CAN网络是一种现场总线,仅仅支持没有路由和安全控制的广播传输。
大部分应用程序都需要要直接处理原始CAN帧,所以和其它类型的网络一样,CAN网络对所有的用户(而不仅仅是root用户)访问没有任何限制。
由于当前CAN_RAW和CAN_BCM的实现仅仅支持对CAN接口的读写,所以允许所有的用户访问CAN并不影响其它类型网络的安全性。
为了使能非root用户对CAN_RAW和CAN_BCM协议套接字的访问,必须在编译内核的时候选上Kconfig的CAN_RAW_USER/CAN_BCM_USER选项。
3.4网络故障监测--------------------
使用CAN总线的时候,可能会遇到物理和mac(mediaaccesscontrol)层的问题。
为了方便用户分析物理收发器的硬件错误、总线仲裁错误和不同的ECU(ElectricalConversionUnit,电气转换装置)引起的错误,对于底层(物理和mac层)的监测和记录是至关重要的。
拥有精确时间戳的错误监测对于诊断错误是非常重要的。
基于以上原因,CAN接口的驱动可以可以选择性的产生所谓的错误帧,这些帧以和其它的CAN帧一样的方式传递给用户程序。
当一个物理层或者MAC层的错误被(CAN控制器)检测到之后,驱动创建一个相应的错误帧。
错误帧可以被应用程序通过CAN的过滤机制请求得到。
过滤机制允许选择需要的错误帧的类型。
默认情况下,接收错误帧的功能是禁止的。
CAN错误帧的详细格式定义在linux头文件中:
include/linux/can/error.h。
4.如何使用SocketCAN===============
就像TCP/IP协议一样,在使用CAN网络之前你首先需要打开一个套接字。
CAN的套接字使用到了一个新的协议族,所以在调用socket
(2)这个系统函数的时候需要将PF_CAN作为第一个参数。
当前有两个CAN的协议可以选择,一个是原始套接字协议(rawsocketprotocol),另一个是广播管理协议BCM(broadcastmanager)。
你可以这样来打开一个套接字:
s=socket(PF_CAN,SOCK_RAW,CAN_RAW);或者s=socket(PF_CAN,SOCK_DGRAM,CAN_BCM);
在成功创建一个套接字之后,你通常需要使用bind
(2)函数将套接字绑定在某个CAN接口上(这和TCP/IP使用不同的IP地址不同,参见第3章)。
在绑定(CAN_RAW)或连接(CAN_BCM)套接字之后,你可以在套接字上使用read
(2)/write
(2),也可以使用send
(2)/sendto
(2)/sendmsg
(2)和对应的recv*操作。
当然也会有CAN特有的套接字选项,下面将会说明。
基本的CAN帧结构体和套接字地址结构体定义在include/linux/can.h:
/**扩展格式识别符由29位组成。
其格式包含两个部分:
11位基本ID、18位扩展ID。
*ControllerAreaNetworkIdentifierstructure
*
*bit0-28:
CAN识别符(11/29bit)
*bit29:
错误帧标志(0=dataframe,1=errorframe)
*bit30:
远程发送请求标志(1=rtrframe)*bit31:
帧格式标志(0=standard11bit,1=extended29bit)
*/
typedef__u32canid_t;
structcan_frame{canid_tcan_id;/*32bitCAN_ID+EFF/RTR/ERRflags*/
__u8can_dlc;/*数据长度:
0..8*/
__u8data[8]__attribute__((aligned(8)));};
结构体的有效数据在data[]数组中,它的字节对齐是64bit的,所以用户可以比较方便的在data[]中传输自己定义的结构体和共用体。
CAN总线中没有默认的字节序。
在CAN_RAW套接字上调用read
(2),返回给用户空间的数据是一个structcan_frame结构体。
就像PF_PACKET套接字一样,sockaddr_can结构体也有接口的索引,这个索引绑定了特定接口:
structsockaddr_can{sa_family_tcan_family;
intcan_ifindex;
union{/*transportprotocolclassaddressinfo(e.g.ISOTP)*/
struct{canid_trx_id,tx_id;}tp;
/*reservedforfutureCANprotocolsaddressinformation*/}can_addr;};
指定接口索引需要调用ioctl()(比如对于没有错误检查CAN_RAW套接字):
ints;structsockaddr_canaddr;structifreqifr;
s=socket(PF_CAN,SOCK_RAW,CAN_RAW);
strcpy(ifr.ifr_name,"can0");ioctl(s,SIOCGIFINDEX,&ifr);
addr.can_family=AF_CAN;addr.can_ifindex=ifr.ifr_ifindex;
bind(s,(structsockaddr*)&addr,sizeof(addr));
(..)
为了将套接字和所有的CAN接口绑定,接口索引必须是0。
这样套接字便可以从所有使能的CAN接口接收CAN帧。
recvfrom
(2)可以指定从哪个接口接收。
在一个已经和所有CAN接口绑定的套接字上,sendto
(2)可以指定从哪个接口发送。
从一个CAN_RAW套接字上读取CAN帧也就是读取structcan_frame结构体:
structcan_frameframe;
nbytes=read(s,&frame,sizeof(structcan_frame));
if(nbytes<0){perror("canrawsocketread");return1;}
/*paranoidcheck...*/if(nbytes<sizeof(structcan_frame)){fprintf(stderr,"read:
incompleteCANframe\n");return1;}
/*dosomethingwiththereceivedCANframe*/
写CAN帧也是类似的,需要用到write
(2)函数:
nbytes=write(s,&frame,sizeof(structcan_frame));
如果套接字跟所有的CAN接口都绑定了(addr.can_index=0),推荐使用recvfrom
(2)获取数据源接口的信息:
structsockaddr_canaddr;structifreqifr;socklen_tlen=sizeof(addr);structcan_frameframe;
nbytes=recvfrom(s,&frame,sizeof(structcan_frame),0,(structsockaddr*)&addr,&len);
/*getinterfacenameofthereceivedCANframe*/ifr.ifr_ifindex=addr.can_ifindex;ioctl(s,SIOCGIFNAME,&ifr);printf("ReceivedaCANframefrominterface%s",ifr.ifr_name);
对于绑定了所有接口的套接字,向某个端口发送数据必须指定接口的详细信息:
strcpy(ifr.ifr_name,"can0");ioctl(s,SIOCGIFINDEX,&ifr);addr.can_ifindex=ifr.ifr_ifindex;addr.can_family=AF_CAN;
nbytes=sendto(s,&frame,sizeof(structcan_frame),0,(structsockaddr*)&addr,sizeof(addr));
4.1使用can_filter的原始套接字(RAWsocket)----------------------------------------------------
CAN_RAW套接字的用法和CAN字符设备的用法是类似的。
为了使用CAN套接字的新特性,在绑定原始套接字的时候将会默认开启以下特性:
-filter将会接收所有的数据-套接字仅仅接收有效的数据帧(=>noerrorframes)-发送帧的回环功能被开启(参见3.2节)-(回环模式下)套接字不接收它自己发送的帧
这些特性的设置可以在绑定之前和之后修改。
为了使用CAN_RAW套接字相关的选项,必须包含<linux/can/raw.h>。
4.1.1原始套接字选项CAN_RAW_FILTER
CAN_RAW套接字的接收可以使用CAN_RAW_FILTER套接字选项指定的多个过滤规则(过滤器)来过滤。
过滤规则(过滤器)的定义在include/linux/can.h中:
structcan_filter{canid_tcan_id;canid_tcan_mask;};
过滤规则的匹配:
<received_can_id>&mask==can_id&mask
/*#defineCAN_INV_FILTER0x20000000U/*tobesetincan_filter.can_id*/#defineCAN_ERR_FLAG0x20000000U/*errorframe*/*/这和大家所熟知的CAN控制器硬件过滤非常相似。
可以使用CAN_INV_FILTER这个宏将can_filter结构体的成员can_id中的比特位反转。
和CAN控制器的硬件过滤形成鲜明对比的是,用户可以为每一个打开的套接字设置多个独立的过滤规则(过滤器):
/*/*validbitsinCANIDforframeformats*/#defineCAN_SFF_MASK0x000007FFU/*标准帧格式(SFF)*/
#defineCAN_EFF_MASK0x1FFFFFFFU/*扩展帧格式(EFF)*/
#defineCAN_ERR_MASK0x1FFFFFFFU/*忽略EFF,RTR,ERR标志*/*/
structcan_filterrfilter[2];
rfilter[0].can_id=0x123;rfilter[0].can_mask=CAN_SFF_MASK;rfilter[1].can_id=0x200;rfilter[1].can_mask=0x700;
setsockopt(s,SOL_CAN_RAW,CAN_RAW_FILTER,&rfilter,sizeof(rfilter));
为了在指定的CAN_RAW套接字上禁用接收过滤规则,可以这样:
setsockopt(s,SOL_CAN_RAW,CAN_RAW_FILTER,NULL,0);
在一些极端情况下不需要读取数据,可以把过滤规则清零(所有成员设为0),这样原始套接字就会忽略接收到的CAN帧。
在这种仅仅发送数据(不读取)的应用中可以在内核中省略接收队列,以此减少CPU的负载(虽然只能减少一点点)。
4.1.2原始套接字选项CAN_RAW_ERR_FILTER正如3.4节所说,CAN接口驱动可以选择性的产生错误帧,错误帧和正常帧以相同的方式传给应用程序。
可能产生的错误被分为不同的种类,使用适当的错误掩码可以过滤它们。
为了注册所有可能的错误情况,CAN_ERR_MASK(0x1FFFFFFFU)这个宏可以用来作为错误掩码。
这个错误掩码定义在linux/can/error.h。
can_err_mask_terr_mask=(CAN_ERR_TX_TIMEOUT|CAN_ERR_BUSOFF);
setsockopt(s,SOL_CAN_RAW,CAN_RAW_ERR_FILTER,&err_mask,sizeof(err_mask));
4.1.3原始套接字选项CAN_RAW_LOOPBACK为了满足众多应用程序的需要,本地回环功能默认是开启的(详细情况参考3.2节)。
但是在一些嵌入式应
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Linux内核Socket CAN中文文档 Linux 内核 Socket CAN 中文 文档