您的位置:首页 > 运维架构 > Linux

嵌入式Linux驱动学习之路(二十五)虚拟网卡驱动程序

2016-11-09 16:57 661 查看
一、协议栈层次对比



设备无关层到驱动层的体系结构



1)、网络协议接口层向网络层协议提供提供统一的数据包收发接口,不论上层协议为ARP还是IP,都通过dev_queue_xmit()函数发送数据,并通过netif_rx()函数接受数据。这一层的存在使得上层协议独立于具体的设备。
2)、网络设备接口层向协议接口层提供统一的用于描述具体网络设备属性和操作的结构体net_device,该结构体是设备驱动功能层中各函数的容器。实际上,网络设备接口层从宏观上规划了具体操作硬件的设备驱动功能层的结构。
3)、设备驱动功能层各函数是网络设备接口层net_device数据结构的具体成员,是驱使网络设备硬件完成相应动作的程序,他通过hard_start_xmit()函数启动发送操作,并通过网络设备上的中断触发接受操作。
4)、网络设备与媒介层是完成数据包发送和接受的物理实体,包括网络适配器和具体的传输媒介,网络适配器被驱动功能层中的函数物理上驱动。对于Linux系统而言,网络设备和媒介都可以是虚拟的。

网络协议接口层:主要进行数据包的收发。

驱动程序代码:

/*************************************************************************
> File Name: s3c_virnet.c
> Author:
> Mail:
> Created Time: 2016年11月09日 星期三 13时27分51秒
************************************************************************/
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/in.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/ip.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>

static struct net_device *vnet_dev;

static void virnet_tx_packet(struct sk_buff *skb, struct net_device *dev)
{
unsigned char *type;
struct iphdr *ih;
__be32 *saddr, *daddr, tmp;
unsigned char tmp_dev_addr[ETH_ALEN];
struct ethhdr *ethhdr;

struct sk_buff *rx_skb;

/* 对调MAC地址 */
ethhdr = (struct ethhdr *)skb->data;
memcpy(tmp_dev_addr,ethhdr->h_dest,ETH_ALEN);
memcpy(ethhdr->h_dest,ethhdr->h_source,ETH_ALEN);
memcpy(ethhdr->h_source,tmp_dev_addr,ETH_ALEN);

/*  */
ih = (struct iphdr *)(skb->data+sizeof(struct ethhdr));
saddr = &ih->saddr;
daddr = &ih->daddr;

tmp = *saddr;
*saddr = *daddr;
*daddr = tmp;

type = skb->data + sizeof(struct ethhdr)+sizeof(struct iphdr);

*type = 0;

ih->check = 0;
ih->check = ip_fast_csum((unsigned char*)ih,ih->ihl);

rx_skb = dev_alloc_skb(skb->len + 2);
skb_reserve(rx_skb,2);
memcpy(skb_put(rx_skb,skb->len),skb->data,skb->len);

rx_skb->dev = dev;
rx_skb->protocol = eth_type_trans(rx_skb,dev);
rx_skb->ip_summed = CHECKSUM_UNNECESSARY;
dev->stats.rx_bytes += skb->len;
dev->stats.rx_packets++;
netif_rx(rx_skb);

}
static int virnet_send_packet(struct sk_buff *skb, struct net_device *dev)
{
dev->stats.tx_packets++;
dev->stats.tx_bytes += skb->len;

netif_stop_queue(dev);  //停止该网卡的队列
/*把skb的数据写入网卡*/
//dev_kfree_skb(skb);     //释放skb
netif_wake_queue(dev);   //数据发送成功后唤醒队列
/* 构造出一个假的sk_buff */

virnet_tx_packet(skb,dev);

return 0;
}

static int s3c_virnet_init(void)
{

/* 分配一个net_device结构体 */
vnet_dev = alloc_netdev(0, "vnet%d", ether_setup);

/* 设置 */
vnet_dev->hard_start_xmit = virnet_send_packet;

/* MAC地址设置 */
vnet_dev->dev_addr[0] = 0x07;
vnet_dev->dev_addr[1] = 0x05;
vnet_dev->dev_addr[2] = 0x06;
vnet_dev->dev_addr[3] = 0x07;
vnet_dev->dev_addr[4] = 0x08;
vnet_dev->dev_addr[5] = 0x09;

/* 添加下面的配置后才能ping通 */
vnet_dev->flags |= IFF_NOARP;
vnet_dev->features |= NETIF_F_NO_CSUM;

/* 注册 */
register_netdev(vnet_dev);

return 0;
}

static void s3c_virnet_exit(void)
{
unregister_netdev(vnet_dev);

free_netdev(vnet_dev);
}

module_init(s3c_virnet_init);
module_exit(s3c_virnet_exit);
MODULE_LICENSE("GPL");


sd
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: