Linux Socket CAN驱动

          现在我们来分析一下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机制列表中后,那么如何去调用它呢?接下来就是中断做的事情了。在中断处理函数d_can_isr中,我们通过napi_schedule()函数调度已经在NAPI机制列表中的d_can_poll()函数。该函数会通过轮询的方式接收数据。而根据NAPI机制,当中断产生后,会调度轮询机制同时关闭所有的中断。流程如下图:

                                                           Linux Socket CAN驱动_第1张图片

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

                                     &"a > 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接收数据的分析,并没有涉及到用户空间的调用。

你可能感兴趣的:(Linux Socket CAN驱动)