轉載請注明出處:http://blog.csdn.net/Righthek 謝謝!
對於本文,我們將從用戶層使用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);
return err ?: __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[i] << (8 * i));
else{
if(i < 4)
dataA|= (frame->data[i] << (8 * i));
else
dataB|= (frame->data[i] << (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的數據接收!
轉載請注明出處:http://blog.csdn.net/Righthek 謝謝!