对Linux Socket CAN的理解(3)-Socket CAN发送数据流程

来源:互联网 发布:淘宝网上银行 编辑:程序博客网 时间:2024/06/05 09:05
        对于本文,我们将从用户层使用Socket CAN进行数据发送时,数据从用户空间到底层驱动的整个通信流程,用户层使用Socket CAN可参考上文章《对Socket CAN的理解(2)-Socket的原理及使用》。

    当我们在用户层通过socket进行CAN数据的发送时,需要进行以下操作:
    (1) 创建一个套接字socket,采用AF_CAN协议;
    (2)将创建的套接字返回描述符sockfd,绑定到本地的地址;
    (3)通过sendto系统调用函数进行发送;


        sendto的函数声明如下:
        int sendto(int sockfd, const void *msg, intlen,unsigned intflags, const struct sockaddr *to, int tolen);
         主要参数说明如下:
         sockfd:通过socket函数生成的套接字描述符;
         msg:该指针指向需要发送数据的缓冲区;
         len:是发送数据的长度;
         to:目标主机的IP地址及端口号信息;

            sendto的系统调用会发送一帧数据报到指定的地址,在CAN协议调用之前把该地址移到内核空间和检查用户空间数据域是否可读。
在net/socket.c源文件中,sendto函数的系统调用如下代码:

SYSCALL_DEFINE6(sendto, int, fd, void __user *, buff, size_t, len, unsigned, flags, structsockaddr __user *, addr, int, addr_len)
{
         structsocket *sock;
         structsockaddr_storage address;
         interr;
         structmsghdr msg;
         structiovec iov;
         intfput_needed;
         if(len > INT_MAX)
                   len= INT_MAX;
         sock= sockfd_lookup_light(fd, &err, &fput_needed);
         if(!sock)
                   gotoout;
         iov.iov_base= buff;
         iov.iov_len= len;
         msg.msg_name= NULL;
         msg.msg_iov= &iov;
         msg.msg_iovlen= 1;
         msg.msg_control= NULL;
         msg.msg_controllen= 0;
         msg.msg_namelen= 0;
         /*把用户空间的地址移动到内核空间中*/
         if(addr) {
                   err= move_addr_to_kernel(addr, addr_len,(struct sockaddr *)&address);
                   if(err < 0)
                            gotoout_put;
                   msg.msg_name= (struct sockaddr *)&address;
                   msg.msg_namelen= addr_len;
         }
         if(sock->file->f_flags & O_NONBLOCK)
                   flags|= MSG_DONTWAIT;
         msg.msg_flags= flags;
         err= sock_sendmsg(sock, &msg, len);
}

        在sendto的系统调用(sys_sendto)里,会调用到sock_sendmsg()函数,该函数代码如下:
int sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
{
         structkiocb iocb;
         structsock_iocb siocb;
         intret;
         init_sync_kiocb(&iocb,NULL);
         iocb.private= &siocb;
         ret= __sock_sendmsg(&iocb, sock, msg, size);
         if(-EIOCBQUEUED == ret)
                   ret= wait_on_sync_kiocb(&iocb);
         returnret;
}

        接下来调用__sock_sendmsg()函数。
static inline int __sock_sendmsg(struct kiocb *iocb,struct socket *sock, struct msghdr *msg, size_t size)
{
         interr = security_socket_sendmsg(sock, msg, size);
         returnerr ?: __sock_sendmsg_nosec(iocb, sock, msg, size);
}

再往下一步就是__sock_sendmsg_nosec函数。在__sock_sendmsg_nosec()函数中会返回一个sendmsg函数指针。
static inline int __sock_sendmsg_nosec(struct kiocb*iocb, struct socket *sock, struct msghdr *msg, size_t size)
{
         structsock_iocb *si = kiocb_to_siocb(iocb);
         sock_update_classid(sock->sk);
         si->sock= sock;
         si->scm= NULL;
         si->msg= msg;
         si->size= size;
         returnsock->ops->sendmsg(iocb, sock,msg, size);
}

        在/net/can/raw.c源文件中,将raw_sendmsg函数地址赋给sendmsg函数指针,即在函数__sock_sendmsg_nosec()中return sock->ops->sendmsg(iocb,sock, msg, size),返回的函数指针将指向raw_sendmsg()函数。
static const struct proto_ops raw_ops = {
         .family        = PF_CAN,
         .release       = raw_release,
         .bind          = raw_bind,
         .connect       = sock_no_connect,
         .socketpair    = sock_no_socketpair,
         .accept        = sock_no_accept,
         .getname       = raw_getname,
         .poll          = datagram_poll,
         .ioctl         = can_ioctl,    /* use can_ioctl() from af_can.c */
         .listen        = sock_no_listen,
         .shutdown      = sock_no_shutdown,
         .setsockopt    = raw_setsockopt,
         .getsockopt    = raw_getsockopt,
         .sendmsg       = raw_sendmsg,
         .recvmsg       = raw_recvmsg,
         .mmap          = sock_no_mmap,
         .sendpage      = sock_no_sendpage,
};

static int raw_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_tsize)
{
         structsock *sk = sock->sk;
         structraw_sock *ro = raw_sk(sk);
         structsk_buff *skb;
         structnet_device *dev;
         intifindex;
         interr;
         if(msg->msg_name) {
                   structsockaddr_can *addr =
                            (structsockaddr_can *)msg->msg_name;
                   if(msg->msg_namelen < sizeof(*addr))
                            return-EINVAL;
                   if(addr->can_family != AF_CAN)
                            return-EINVAL;
                   ifindex= addr->can_ifindex;
         }else
                   ifindex= ro->ifindex;
         if(size != sizeof(struct can_frame))
                   return-EINVAL;
         dev= dev_get_by_index(&init_net, ifindex);
         if(!dev)
                   return-ENXIO;
         skb= sock_alloc_send_skb(sk, size, msg->msg_flags & MSG_DONTWAIT,
                                       &err);
         if(!skb)
                   gotoput_dev;
         err= memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
         if(err < 0)
                   gotofree_skb;
         err= sock_tx_timestamp(sk, &skb_shinfo(skb)->tx_flags);
         if(err < 0)
                   gotofree_skb;
         /*to be able to check the received tx sock reference in raw_rcv() */
         skb_shinfo(skb)->tx_flags|= SKBTX_DRV_NEEDS_SK_REF;
         skb->dev= dev;
         skb->sk  = sk;
         err= can_send(skb,ro->loopback);
         dev_put(dev);
         if(err)
                   gotosend_failed;
         returnsize;
}

         在net/can/af_can.c源文件中,can_send函数负责CAN协议层的数据传输,即传输一帧CAN报文(可选本地回环)。参数skb指针指向套接字缓冲区和在数据段的CAN帧。loop参数是在本地CAN套接字上为监听者提供回环。
int can_send(struct sk_buff *skb, int loop)
{
         structsk_buff *newskb = NULL;
         structcan_frame *cf = (struct can_frame *)skb->data;
         interr;
         if(skb->len != sizeof(struct can_frame) || cf->can_dlc > 8) {
                   kfree_skb(skb);
                   return-EINVAL;
         }
         if(skb->dev->type != ARPHRD_CAN) {
                   kfree_skb(skb);
                   return-EPERM;
         }
         if(!(skb->dev->flags & IFF_UP)) {
                   kfree_skb(skb);
                   return-ENETDOWN;
         }
         skb->protocol= htons(ETH_P_CAN);
         skb_reset_network_header(skb);
         skb_reset_transport_header(skb);
         if(loop) {
                   /*local loopback of sent CAN frames */
                   /*indication for the CAN driver: do loopback */
                   skb->pkt_type= PACKET_LOOPBACK;
                   if(!(skb->dev->flags & IFF_ECHO)) {
                            /*
                             * If the interface is not capable to doloopback
                             * itself, we do it here.
                             */
                            newskb= skb_clone(skb, GFP_ATOMIC);
                            if(!newskb) {
                                     kfree_skb(skb);
                                     return-ENOMEM;
                            }
                            newskb->sk= skb->sk;
                            newskb->ip_summed= CHECKSUM_UNNECESSARY;
                            newskb->pkt_type= PACKET_BROADCAST;
                   }
         }else {
                   /*indication for the CAN driver: no loopback required */
                   skb->pkt_type= PACKET_HOST;
         }
         /*send to netdevice */
         err= dev_queue_xmit(skb);
         if(err > 0)
                   err= net_xmit_errno(err);
         if(err) {
                   kfree_skb(newskb);
                   returnerr;
         }
         if(newskb)
                   netif_rx_ni(newskb);
         /*update statistics */
         can_stats.tx_frames++;
         can_stats.tx_frames_delta++;
         return0;
}

int dev_queue_xmit(struct sk_buff *skb)
{
         structnet_device *dev = skb->dev;
         structnetdev_queue *txq;
         structQdisc *q;
         intrc = -ENOMEM;

         /*Disable soft irqs for various locks below. Also
          * stops preemption for RCU.
          */
         rcu_read_lock_bh();

         txq= dev_pick_tx(dev, skb);
         q= rcu_dereference_bh(txq->qdisc);

#ifdef CONFIG_NET_CLS_ACT
         skb->tc_verd= SET_TC_AT(skb->tc_verd, AT_EGRESS);
#endif
         trace_net_dev_queue(skb);
         if(q->enqueue) {
                   rc= __dev_xmit_skb(skb, q, dev, txq);
                   gotoout;
         }
         if(dev->flags & IFF_UP) {
                   intcpu = smp_processor_id(); /* ok because BHs are off */

                   if(txq->xmit_lock_owner != cpu) {

                            if(__this_cpu_read(xmit_recursion) > RECURSION_LIMIT)
                                     gotorecursion_alert;

                            HARD_TX_LOCK(dev,txq, cpu);

                            if(!netif_tx_queue_stopped(txq)) {
                                     __this_cpu_inc(xmit_recursion);
                                     rc= dev_hard_start_xmit(skb, dev, txq);
                                     __this_cpu_dec(xmit_recursion);
                                     if(dev_xmit_complete(rc)) {
                                               HARD_TX_UNLOCK(dev,txq);
                                               gotoout;
                                     }
                            }
                            HARD_TX_UNLOCK(dev,txq);
                            if(net_ratelimit())
                                     printk(KERN_CRIT"Virtual device %s asks to "
                                            "queue packet!\n", dev->name);
                   }else {
                            /*Recursion is detected! It is possible,
                             * unfortunately
                             */
recursion_alert:
                            if(net_ratelimit())
                                     printk(KERN_CRIT"Dead loop on virtual device "
                                            "%s, fix it urgently!\n",dev->name);
                   }
         }

         rc= -ENETDOWN;
         rcu_read_unlock_bh();

         kfree_skb(skb);
         returnrc;
out:
         rcu_read_unlock_bh();
         returnrc;
}

int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev, structnetdev_queue *txq)
{
         conststruct net_device_ops *ops = dev->netdev_ops;
         intrc = NETDEV_TX_OK;
         unsignedint skb_len;
         if(likely(!skb->next)) {
                   u32features;
                   /*
                    * If device doesn't need skb->dst, releaseit right now while
                    * its hot in this cpu cache
                    */
                   if(dev->priv_flags & IFF_XMIT_DST_RELEASE)
                            skb_dst_drop(skb);
                   if(!list_empty(&ptype_all))
                            dev_queue_xmit_nit(skb,dev);
                   skb_orphan_try(skb);
                   features= netif_skb_features(skb);

                   if(vlan_tx_tag_present(skb) &&
                       !(features & NETIF_F_HW_VLAN_TX)) {
                            skb = __vlan_put_tag(skb,vlan_tx_tag_get(skb));
                            if(unlikely(!skb))
                                     gotoout;
                            skb->vlan_tci= 0;
                   }
                   if(netif_needs_gso(skb, features)) {
                            if(unlikely(dev_gso_segment(skb, features)))
                                     gotoout_kfree_skb;
                            if(skb->next)
                                     gotogso;
                   }else {
                            if(skb_needs_linearize(skb, features) &&
                                __skb_linearize(skb))
                                     gotoout_kfree_skb;
                            /*If packet is not checksummed and device does not
                             * support checksumming for this protocol,complete
                             * checksumming here.
                             */
                            if(skb->ip_summed == CHECKSUM_PARTIAL) {
                                     skb_set_transport_header(skb,
                                               skb_checksum_start_offset(skb));
                                     if(!(features & NETIF_F_ALL_CSUM) &&
                                          skb_checksum_help(skb))
                                               gotoout_kfree_skb;
                            }
                   }
                   skb_len= skb->len;
                   rc= ops->ndo_start_xmit(skb, dev);
                   trace_net_dev_xmit(skb,rc, dev, skb_len);
                   if(rc == NETDEV_TX_OK)
                            txq_trans_update(txq);
                   returnrc;
         }
}

         以下开始进行到CAN的底层驱动代码了,由于CAN驱动是编译进内核中,所以在系统启动时会注册CAN驱动,注册CAN驱动过程中会初始化d_can_netdev_ops结构体变量。在这个过程中,d_can_netdev_ops结构体变量定义了3个函数指针,其中(*ndo_start_xmit)函数指针指向d_can_start_xmit函数的入口地址。
static const struct net_device_ops d_can_netdev_ops = {
         .ndo_open= d_can_open,
         .ndo_stop= d_can_close,
         .ndo_start_xmit =d_can_start_xmit,
};

static netdev_tx_t d_can_start_xmit(struct sk_buff*skb, structnet_device *dev)
{
         u32msg_obj_no;
         structd_can_priv *priv = netdev_priv(dev);
         structcan_frame *frame = (struct can_frame *)skb->data;
         if(can_dropped_invalid_skb(dev, skb))
                   returnNETDEV_TX_OK;
         msg_obj_no= get_tx_next_msg_obj(priv);
         /*prepare message object for transmission */
         d_can_write_msg_object(dev,D_CAN_IF_TX_NUM, frame, msg_obj_no);
         can_put_echo_skb(skb,dev, msg_obj_no - D_CAN_MSG_OBJ_TX_FIRST);
         /*
          * we have to stop the queue in case of a wraparound or
          * if the next TX message object is still inuse
          */
         priv->tx_next++;
         if(d_can_is_next_tx_obj_busy(priv, get_tx_next_msg_obj(priv)) ||
                   ((priv->tx_next& D_CAN_NEXT_MSG_OBJ_MASK) == 0))
                   netif_stop_queue(dev);
         returnNETDEV_TX_OK;
}

         在d_can_start_xmit()函数中,会调用d_can_write_msg_object()函数准备消息报文进行传输。
static void d_can_write_msg_object(struct net_device *dev, intiface, struct can_frame *frame, int objno)
{
         inti;
         unsignedint id;
         u32dataA = 0;
         u32dataB = 0;
         u32flags = 0;
         structd_can_priv *priv = netdev_priv(dev);

         if(!(frame->can_id & CAN_RTR_FLAG))
                   flags|= D_CAN_IF_ARB_DIR_XMIT;

         if(frame->can_id & CAN_EFF_FLAG) {
                   id= frame->can_id & CAN_EFF_MASK;
                   flags|= D_CAN_IF_ARB_MSGXTD;
         }else
                   id= ((frame->can_id & CAN_SFF_MASK) << 18);

         flags|= D_CAN_IF_ARB_MSGVAL;
         d_can_write(priv,D_CAN_IFARB(iface), IFX_WRITE_IDR(id) | flags);

         for(i = 0; i < frame->can_dlc; i++) {
                   if(frame->can_dlc <= 4)
                            dataA|= (frame->data << (8 * i));
                   else{
                            if(i < 4)
                                     dataA|= (frame->data << (8 * i));
                            else
                                     dataB|= (frame->data << (8 * (i - 4)));
                   }
         }

         /*DATA write to Message object registers DATAA and DATAB */
         if(frame->can_dlc <= 4)
                   d_can_write(priv,D_CAN_IFDATA(iface), dataA);
         else{
                   d_can_write(priv,D_CAN_IFDATB(iface), dataB);
                   d_can_write(priv,D_CAN_IFDATA(iface), dataA);
         }

         /*enable TX interrupt for this message object */
         d_can_write(priv,D_CAN_IFMCTL(iface),
                            D_CAN_IF_MCTL_TXIE| D_CAN_IF_MCTL_EOB |
                            D_CAN_IF_MCTL_TXRQST| D_CAN_IF_MCTL_NEWDAT |
                            frame->can_dlc);

         /*Put message data into message RAM */
         d_can_object_put(dev,iface, objno, D_CAN_IF_CMD_ALL);
}
    以上即是作者对Socket CAN进行数据发送的理解。接下来,我们将分析Socket CAN的数据接收!
0 0
原创粉丝点击