转载请注明出处:http://blog.csdn.net/Righthek 谢谢!

如今我们来分析一下CAN总线的接收数据流程,对于网络设备。数据接收大体上採用中断+NAPI机制进行数据的接收。相同。我们如今的CAN模块也是採用相同的方式进行数据的接收。

因为我们仅仅针对CAN总线接收数据这条主线进行分析。

因些。会忽略一些针对CAN协议的设置及初始化等相关代码。

在初始化CAN设备时,我们须要给CAN设备分配NAPI功能。我们通过netif_napi_add()函数将CAN设备加入到NAPI机制列表中。源代码例如以下:

struct net_device *alloc_d_can_dev(intnum_objs)

{

structnet_device *dev;

structd_can_priv *priv;

dev= alloc_candev(sizeof(struct d_can_priv), num_objs/2);

if(!dev)

returnNULL;

priv= netdev_priv(dev);

netif_napi_add(dev, &priv->napi,d_can_poll, num_objs/2);

priv->dev= dev;

priv->can.bittiming_const= &d_can_bittiming_const;

priv->can.do_set_mode= d_can_set_mode;

priv->can.do_get_berr_counter= d_can_get_berr_counter;

priv->can.ctrlmode_supported= (CAN_CTRLMODE_LOOPBACK |

CAN_CTRLMODE_LISTENONLY|

CAN_CTRLMODE_BERR_REPORTING|

CAN_CTRLMODE_3_SAMPLES);

returndev;

}

以上将CAN设备加入到NAPI机制列表中后。那么怎样去调用它呢?(关于NAPI机制,请查看文章《以前的足迹——对CAN驱动中的NAPI机制的理解》)接下来就是中断做的事情了。在中断处理函数d_can_isr中,我们通过napi_schedule()函数调度已经在NAPI机制列表中的d_can_poll()函数。

该函数会通过轮询的方式接收数据。

而依据NAPI机制,其中断产生后,会调度轮询机制同一时候关闭全部的中断。流程例如以下图:

static irqreturn_t d_can_isr(intirq, void *dev_id)

{

structnet_device *dev = (struct net_device *)dev_id;

structd_can_priv *priv = netdev_priv(dev);

priv->irqstatus= d_can_read(priv, D_CAN_INT);

if(!priv->irqstatus)

returnIRQ_NONE;

/*disable all interrupts and schedule the NAPI */

d_can_interrupts(priv,DISABLE_ALL_INTERRUPTS);

napi_schedule(&priv->napi);

returnIRQ_HANDLED;

}

其中断产生时。会调用下面函数d_can_poll()。该函数即採用轮询的方式进行数据的接收。因为CAN总线状态中断具有最高优先权。在接收数据之前,须要对CAN总线的状态进行推断。而对于CAN总线错误状态有三种:

(1)      主动错误。

(2)      被动错误。

(3)      总线关闭;

static int d_can_poll(structnapi_struct *napi, int quota)

{

intlec_type = 0;

intwork_done = 0;

structnet_device *dev = napi->dev;

structd_can_priv *priv = netdev_priv(dev);

if(!priv->irqstatus)

gotoend;

/*status events have the highest priority */

if(priv->irqstatus == STATUS_INTERRUPT) {

priv->current_status= d_can_read(priv, D_CAN_ES);

/*handle Tx/Rx events */

if(priv->current_status & D_CAN_ES_TXOK)

d_can_write(priv,D_CAN_ES,

priv->current_status& ~D_CAN_ES_TXOK);

if(priv->current_status & D_CAN_ES_RXOK)

d_can_write(priv,D_CAN_ES,

priv->current_status& ~D_CAN_ES_RXOK);

/*handle state changes */

if((priv->current_status & D_CAN_ES_EWARN) &&

(!(priv->last_status& D_CAN_ES_EWARN))) {

netdev_dbg(dev,"entered error warning state\n");

work_done+= d_can_handle_state_change(dev,

D_CAN_ERROR_WARNING);

}

if((priv->current_status & D_CAN_ES_EPASS) &&

(!(priv->last_status& D_CAN_ES_EPASS))) {

netdev_dbg(dev,"entered error passive state\n");

work_done+= d_can_handle_state_change(dev,

D_CAN_ERROR_PASSIVE);

}

if((priv->current_status & D_CAN_ES_BOFF) &&

(!(priv->last_status& D_CAN_ES_BOFF))) {

netdev_dbg(dev,"entered bus off state\n");

work_done +=d_can_handle_state_change(dev,

D_CAN_BUS_OFF);

}

/*handle bus recovery events */

if((!(priv->current_status & D_CAN_ES_BOFF)) &&

(priv->last_status& D_CAN_ES_BOFF)) {

netdev_dbg(dev,"left bus off state\n");

priv->can.state= CAN_STATE_ERROR_ACTIVE;

}

if((!(priv->current_status & D_CAN_ES_EPASS)) &&

(priv->last_status& D_CAN_ES_EPASS)) {

netdev_dbg(dev,"left error passive state\n");

priv->can.state= CAN_STATE_ERROR_ACTIVE;

}

priv->last_status= priv->current_status;

/*handle lec errors on the bus */

lec_type= d_can_has_handle_berr(priv);

if(lec_type)

work_done+= d_can_handle_bus_err(dev, lec_type);

}else if ((priv->irqstatus >= D_CAN_MSG_OBJ_RX_FIRST) &&

(priv->irqstatus<= D_CAN_MSG_OBJ_RX_LAST)) {

/*handle events corresponding to receive message objects */

work_done+=
d_can_do_rx_poll(dev, (quota - work_done));

}else if ((priv->irqstatus >= D_CAN_MSG_OBJ_TX_FIRST) &&

(priv->irqstatus<= D_CAN_MSG_OBJ_TX_LAST)) {

/*handle events corresponding to transmit message objects */

d_can_do_tx(dev);

}

end:

if(work_done < quota) {

napi_complete(napi);

/*enable all IRQs */

d_can_interrupts(priv,ENABLE_ALL_INTERRUPTS);

}

returnwork_done;

}

当总线状态数据状态正常时,进行数据的接收。

static int d_can_do_rx_poll(structnet_device *dev, int quota)

{

structd_can_priv *priv = netdev_priv(dev);

unsignedint msg_obj, mctrl_reg_val;

u32num_rx_pkts = 0;

u32intpnd_x_reg_val;

u32intpnd_reg_val;

for(msg_obj = D_CAN_MSG_OBJ_RX_FIRST; msg_obj <= D_CAN_MSG_OBJ_RX_LAST

&&quota > 0; msg_obj++) {

intpnd_x_reg_val= D_CAN_GET_XREG_NUM(priv, D_CAN_INTPND_X);

intpnd_reg_val= d_can_read(priv,

D_CAN_INTPND(intpnd_x_reg_val));

/*

* as interrupt pending register's bit n-1corresponds to

* message object n, we need to handle the sameproperly.

*/

if(intpnd_reg_val & (1 << (msg_obj - 1))) {

d_can_object_get(dev,D_CAN_IF_RX_NUM, msg_obj,

D_CAN_IF_CMD_ALL&

~D_CAN_IF_CMD_TXRQST);

mctrl_reg_val= d_can_read(priv,

D_CAN_IFMCTL(D_CAN_IF_RX_NUM));

if(!(mctrl_reg_val & D_CAN_IF_MCTL_NEWDAT))

continue;

/*read the data from the message object */

d_can_read_msg_object(dev, D_CAN_IF_RX_NUM,

mctrl_reg_val);

if(mctrl_reg_val & D_CAN_IF_MCTL_EOB)

d_can_setup_receive_object(dev,D_CAN_IF_RX_NUM,

D_CAN_MSG_OBJ_RX_LAST,0, 0,

D_CAN_IF_MCTL_RXIE| D_CAN_IF_MCTL_UMASK

|D_CAN_IF_MCTL_EOB);

if(mctrl_reg_val & D_CAN_IF_MCTL_MSGLST) {

d_can_handle_lost_msg_obj(dev,D_CAN_IF_RX_NUM,

msg_obj);

num_rx_pkts++;

quota--;

continue;

}

if(msg_obj < D_CAN_MSG_OBJ_RX_LOW_LAST)

d_can_mark_rx_msg_obj(dev,D_CAN_IF_RX_NUM,

mctrl_reg_val,msg_obj);

else if (msg_obj >D_CAN_MSG_OBJ_RX_LOW_LAST)

/*activate this msg obj */

d_can_activate_rx_msg_obj(dev,D_CAN_IF_RX_NUM,

mctrl_reg_val,msg_obj);

elseif (msg_obj == D_CAN_MSG_OBJ_RX_LOW_LAST)

/*activate all lower message objects */

d_can_activate_all_lower_rx_msg_objs(dev,

D_CAN_IF_RX_NUM,mctrl_reg_val);

num_rx_pkts++;

quota--;

}

}

returnnum_rx_pkts;

}

下面函数是从CAN模块的接收寄存器中接收数据。

static int d_can_read_msg_object(structnet_device *dev, int iface, int ctrl)

{

inti;

u32dataA = 0;

u32dataB = 0;

unsignedint arb_val;

unsignedint mctl_val;

structd_can_priv *priv = netdev_priv(dev);

structnet_device_stats *stats = &dev->stats;

structsk_buff *skb;

structcan_frame *frame;

skb= alloc_can_skb(dev, &frame);

if(!skb) {

stats->rx_dropped++;

return-ENOMEM;

}

frame->can_dlc= get_can_dlc(ctrl & 0x0F);

arb_val= d_can_read(priv, D_CAN_IFARB(iface));

mctl_val= d_can_read(priv, D_CAN_IFMCTL(iface));

if(arb_val & D_CAN_IF_ARB_MSGXTD)

frame->can_id= (arb_val & CAN_EFF_MASK) | CAN_EFF_FLAG;

else

frame->can_id= (arb_val >> 18) & CAN_SFF_MASK;

if(mctl_val & D_CAN_IF_MCTL_RMTEN)

frame->can_id|= CAN_RTR_FLAG;

else{

dataA= d_can_read(priv, D_CAN_IFDATA(iface));

dataB= d_can_read(priv, D_CAN_IFDATB(iface));

for(i = 0; i < frame->can_dlc; i++) {

/*Writing MO higher 4 data bytes to skb */

if(frame->can_dlc <= 4)

frame->data[i]= dataA >> (8 * i);

else{

if(i < 4)

frame->data[i]= dataA >> (8 * i);

else

frame->data[i] = dataB >> (8 *(i-4));

}

}

}

netif_receive_skb(skb);

stats->rx_packets++;

stats->rx_bytes+= frame->can_dlc;

return0;

}

以上是对底层CAN接收数据的分析,并没有涉及到用户空间的调用。

转载请注明出处:http://blog.csdn.net/Righthek 谢谢!

对Socket CAN的理解(4)——【Socket CAN接收数据流程】的更多相关文章

  1. SOCKET CAN的理解

    转载请注明出处:http://blog.csdn.net/Righthek 谢谢! CAN总线原理 由于Socket CAN涉及到CAN总线协议.套接字.Linux网络设备驱动等.因此,为了能够全面地 ...

  2. Web性能优化之-深入理解TCP Socket

    什么是Socket?    大家都用电脑上网,当我们访问运维社区https://www.unixhot.com的时候,我们的电脑和运维社区的服务器就会创建一条Socket,我们称之为网络套接字.那么既 ...

  3. socket 进程通讯理解

    [转]https://blog.csdn.net/andrewgithub/article/details/81613120 正如可以给fopen的传入不同参数值,以打开不同的文件.创建socket的 ...

  4. flash as3 socket安全服务网关(socket policy file server)

    关键字: SecurityErrorEvent socket as3 flash有着自己的一套安全处理模式,在socket方面,我这样的菜鸟无法理解他的好处:一句话,不怀好意的人如果想用flash写一 ...

  5. Windows socket之最简单的socket程序

    原文:Windows socket之最简单的socket程序 最简单的服务器的socket程序流程如下(面向连接的TCP连接 ): 1. WSAStartup(); 初始化网络库的使用. 2. soc ...

  6. python 全栈开发,Day33(tcp协议和udp协议,互联网协议与osi模型,socket概念,套接字(socket)初使用)

    先来回顾一下昨天的内容 网络编程开发架构 B/S C/S架构网卡 mac地址网段 ip地址 : 表示了一台电脑在网络中的位置 子网掩码 : ip和子网掩码按位与得到网段 网关ip : 内置在路由器中的 ...

  7. [C#]Socket通信BeginReceive异步接收数据何时回调Callback

    原文地址:http://www.cnblogs.com/wangtonghui/p/3277303.html 最近在做服务器压力测试程序. 接触了一段时间Socket异步通讯,发现自己对BeginRe ...

  8. socket 中午吃的啥 socket 并发服务器 fork

    http://www.cnblogs.com/thinksasa/archive/2013/02/26/2934206.html zh.wikipedia.org/wiki/網路插座 在作業系統中,通 ...

  9. 为什么有监听socket和连接socket,为什么产生两个socket

    为什么有监听socket和连接socket,为什么产生两个socket 先看一半的socket建立连接的双方的过程: 客户端: socket()---->创建出 active_socket_fd ...

随机推荐

  1. Android Bitmap转换WebP图片导致损坏的分析及解决方案

    背景 作为移动领域所力推的图片格式,WebP图片在商业领域证明了其应有的价值.基于其他格式的横向对比,其在压缩性能表现,及还原度极为优秀,节省大量的带宽开销.基于可观的效益比,团队早前已开始磋商将当前 ...

  2. px-em-rem单位转换

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  3. [Windows Server 2003] 初识Windows Server 2003

    ★ 欢迎来到[护卫神·V课堂],网站地址:http://v.huweishen.com ★ 护卫神·V课堂 是护卫神旗下专业提供服务器教学视频的网站,每周更新视频. ★ 本节我们将带领大家:初次见识W ...

  4. Jmeter在windows上安装和环境配置

    一.Jmeter简单介绍 Apache JMeter是Apache组织开发的基于Java的压力测试工具.它可以用于测试静态和动态资源,例如静态文件.Java小服务程序.CGI 脚本.Java 对象.数 ...

  5. 观察者模式在Foundation框架通知中的应用

    GitHub传送门 1.何为观察者模式? 观察者设计模式定义了对象间的一种一对多的依赖关系,以便一个对象的状态发生变化时,所有依赖于它的对象都得到通知并自动刷新. 举个简单的例子:你和你的舍友都订阅了 ...

  6. 日常开发需要掌握的Git命令

    本人待的两家公司,一直都是用的SVN,Git我只是自己私下学习和开发小项目的时候用过,工作一直没有使用过,但还是要学的... Git是最好的分布式版本控制系统 工作流程 SVN和Git的区别 SVN是 ...

  7. Luogu P4016 「 网络流 24 题 」负载平衡问题

    吐槽题目难度,这个题建模好像比前两个都要难,但是难度评级却比第二个要低. 解题思路 依旧是考虑如何建模和建立源点汇点.每个点的货物数量到最后都一样的话肯定是等于他们的平均值.用 $num$ 数组存储原 ...

  8. 关于SPFA的双端队列优化

    7.11 Update 我做题的时候发现这样写会RE 因为在使用双端队列优化SPFA的时候 在将一个点加入队列的时候,如果队列已经空了 那么一旦出现dis[Q.front()]就会RE 可以这样修改 ...

  9. 剑指offer---以O(1)时间删除链表节点

    问题:删除链表节点 要求:以O(1)时间 对于删除指定索引的链表元素大家都很熟悉,思路一般是从头遍历链表直到指定索引位置删除元素,然后维护一下指针即可,时间复杂度O(n).代码如下: // 删除pos ...

  10. CentOS 6 Yum本地源配置

    #cd /etc/yum.repos.d #rm CentOS-Base.repo CentOS-Base.repo 是yum 网络源的配置文件(默认) #vi CentOS-Media.repo C ...