Linux驱动开发(14)------- 网络设备驱动介绍


网络设备驱动概述


【1】Linux网络层次结构

在这里插入图片描述

【2】什么是网络设备

  • 网络设备驱动它负责了数据链路层的一部分工作,最主要的工作就具驱动网络设备(通常也叫网卡)将数据发送出去,或者将网络设备收到的数据往上层递交,更简单地说就是负责网络数据的收发。

  • 我们知道,网络数据是按包为单位来组织的,这样网络设备驱动就和块设备驱动非常类似。网络设备驱动负责将数据包“写入”网络或从网络中“读取”数据包,从而完成上层的请求。

  • 但是,它们之间还是有一些差别的。首先,网络设备没有设备节点,因此没有使用文件系统的那套接口对网络设备进行访问,使用的是另一套套接字编程接口。

  • 其次,网络设备通常是基于中断的方式工作的,在收到数据包后,会产生相应的中断,网络驱动从网卡中获取数据包后进行必要的验证,然后主动将数据包递交给上层。而块设备驱动在读取方向上也是被动地接受上层的请求。

  • 网络设备驱动在这里担当了承上启下的作用,使上层的网络协议层不必关心底层的硬件细节信息另外,驱动本身基本也与协议无关,不需要解析网络数据包,只负责数据包的收发。

  • 当然,除了数据包收发的主要工作之外,网络设备驱动还要负责大量的管理任务,如设置硬件地址、修改传输参数、错误处理和统计、流量控制等。

【3】网络设备接口

  • /dev下没有设备文件,也不通过/sys下的属性文件访问。直观看来,应用层都是通过一些特殊的命令(如ifconfig、ping等)来访问网卡硬件(调用驱动)的。本质上应用调用驱动的方法可以通过分析ping、ifconfig等命令的实现来得知。实际就是通过:socket、bind、listen、connect、send、recv等API来实现的。
  • 网络设备被抽象成一个能够发送和接收数据包的“网络接口”
  • struct net_device来管理所有网络接口

网络设备驱动核心数据结构


【1】网络设备驱动中一个重要的数据结构是struct net_device,这个结构非常庞大,我们也只关心这个结构中和驱动相关的一部分。

struct net_device {
	char			name[IFNAMSIZ]; //网络设备名字,ethx表示以太网,pppx表示连接类型的设备,isdnx表示ISDN卡,lo表示回环设备
	unsigned long		mem_end;	//PCI网卡之类的网络设备的共享内存的结束地址和起始地址
	unsigned long		mem_start;	
	
	unsigned long		base_addr;	//PCI网卡之类的网络设备的IO端口地址
	unsigned int		irq;		//网卡使用的中断号
	struct net_device_stats	stats;  //收发统计信息
	const struct net_device_ops *netdev_ops;   //网络设备的操作方法
	const struct ethtool_ops *ethtool_ops;     //用户层ethtool工具在驱动中对应的操作方法集合

	unsigned int		flags;	           //一组接口的标志
	unsigned int		mtu;	          //接口MTU(最大传输单元)值,对于以太网设备来说一般是1500字节
	unsigned short		type;	          //接口的硬件类型
	unsigned short		hard_header_len;	 //硬件头长度,以太网14字节
	unsigned char		addr_len;	   //硬件地址长度,以太网位MAC地址,长度6字节

	struct netdev_hw_addr_list	uc;    //单播MAC地址列表
	struct netdev_hw_addr_list	mc;     //多播MAC地址列表
};

【2】网络设备的操作方法集合由struct net_device_ops结构体来描述。该结构中有很多函数指针指向不同的函数,用于对网络设备进行不同的操作。我们最关心的还是与数据的发送处理相关的内容,下面也只列出部分内容。


struct net_device_ops {
	int			(*ndo_init)(struct net_device *dev);  //当网络设备注册后,该函数被调用,用于网络设备的后期初始化操作,没有特殊要求则为NULL。
	int			(*ndo_open)(struct net_device *dev);  //当激活网络设备时,该函数被调用
	int			(*ndo_stop)(struct net_device *dev);  //当网络设备被禁用时,该函数被调用
netdev_tx_t		(*ndo_start_xmit) (struct sk_buff *skb, struct net_device *dev); //当一个网络数据包需要发送时,该函数被调用,函数应该返回NETDEV_TX_OK或NETDEV_TX_BUSY。
	
	int			(*ndo_set_mac_address)(struct net_device *dev, void *addr);//当需要改变MAC地址时,该函数被调用,可以为NULL
	int			(*ndo_validate_addr)(struct net_device *dev);//用于验证MAC地址是否合法有效。
	int			(*ndo_do_ioctl)(struct net_device *dev,struct ifreq *ifr, int cmd);//用于处理通用接口代码不能处理的用户请求
	void		(*ndo_tx_timeout) (struct net_device *dev);//当发送超时时,该函数被调用
      ............................
};

网络设备驱动实例------>虚拟网卡


#include <linux/module.h>
#include <linux/sched.h>
#include <linux/kernel.h> /* printk() */
#include <linux/slab.h> /* kmalloc() */
#include <linux/errno.h>  /* error codes */
#include <linux/types.h>  /* size_t */
#include <linux/interrupt.h> /* mark_bh */
#include <linux/in.h>
#include <linux/netdevice.h>   /* struct device, and other headers */
#include <linux/etherdevice.h> /* eth_type_trans */
#include <linux/ip.h>          /* struct iphdr */
#include <linux/tcp.h>         /* struct tcphdr */
#include <linux/skbuff.h>
#include <linux/if_ether.h>
#include <linux/in6.h>
#include <asm/uaccess.h>
#include <asm/checksum.h>
#include <linux/platform_device.h>


// 如果需要随机MAC地址则定义该宏
#define  MAC_AUTO


static struct net_device *astonnet_devs;		

//网络设备结构体,作为net_device->priv
struct astonnet_priv {
    struct net_device_stats stats;		//有用的统计信息
    int status;							//网络设备的状态信息,是发完数据包,还是接收到网络数据包
    int rx_packetlen;					//接收到的数据包长度
    u8 *rx_packetdata;					//接收到的数据
    int tx_packetlen;					//发送的数据包长度
    u8 *tx_packetdata;					//发送的数据
    struct sk_buff *skb;				//socket buffer结构体,网络各层之间传送数据都是通过这个结构体来实现的
    spinlock_t lock;					//自旋锁
};

//网络接口的打开函数
int astonnet_open(struct net_device *dev)
{
	printk("astonnet_open\n");
	
#ifndef MAC_AUTO
	int i;
	for (i=0; i<6; i++)
	    dev->dev_addr[i] = 0xaa;
#else
    random_ether_addr(dev->dev_addr); //随机源地址
#endif
    netif_start_queue(dev);		//打开传输队列,这样才能进行数据传输
	
    return 0;
}

int astonnet_release(struct net_device *dev)
{
    printk("astonnet_release\n");
	//当网络接口关闭的时候,调用stop方法,这个函数表示不能再发送数据
    netif_stop_queue(dev);
	
    return 0;
}

//接包函数
void astonnet_rx(struct net_device *dev, int len, unsigned char *buf)
{   
    struct sk_buff *skb;
    struct astonnet_priv *priv = (struct astonnet_priv *) dev->ml_priv;
    skb = dev_alloc_skb(len+2);//分配一个socket buffer,并且初始化skb->data,skb->tail和skb->head
    if (!skb) {
        printk("gecnet rx: low on mem - packet dropped\n");
        priv->stats.rx_dropped++;
        return;
    }
    skb_reserve(skb, 2); /* align IP on 16B boundary */ 
    memcpy(skb_put(skb, len), buf, len);//skb_put是把数据写入到socket buffer
    /* Write metadata, and then pass to the receive level */
    skb->dev = dev;
    skb->protocol = eth_type_trans(skb, dev);//返回的是协议号
    skb->ip_summed = CHECKSUM_UNNECESSARY; //此处不校验
    priv->stats.rx_packets++;//接收到包的个数+1
    
    priv->stats.rx_bytes += len;//接收到包的长度
    printk("astonnet rx \n");
    netif_rx(skb);//通知内核已经接收到包,并且封装成socket buffer传到上层
    return;
}

//真正的处理的发送数据包
//模拟从一个网络向另一个网络发送数据包
void astonnet_hw_tx(char *buf, int len, struct net_device *dev)

{
    struct net_device *dest;//目标设备结构体,net_device存储一个网络接口的重要信息,是网络驱动程序的核心
    struct astonnet_priv *priv;

    if (len < sizeof(struct ethhdr) + sizeof(struct iphdr)) 
	{
		printk("astonnet: Hmm... packet too short (%i octets)\n", len);
        return;
    }

    dest = astonnet_devs;
    priv = (struct astonnet_priv *)dest->ml_priv;		//目标dest中的priv
    priv->rx_packetlen = len;
    priv->rx_packetdata = buf;

    printk("astonnet tx \n");
    dev_kfree_skb(priv->skb);
}

//发包函数
int astonnet_tx(struct sk_buff *skb, struct net_device *dev)
{
	int len;
    char *data;
    struct astonnet_priv *priv = (struct astonnet_priv *)dev->ml_priv;

    if (skb == NULL)
	{
		printk("net_device %p,  skb %p\n", dev,  skb);
		return 0;
    }

    len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len;//ETH_ZLEN是所发的最小数据包的长度
    data = skb->data;//将要发送的数据包中数据部分
    priv->skb = skb;
    astonnet_hw_tx(data, len, dev);//真正的发送函数
	
	return 0; 
}


//设备初始化函数
int astonnet_init(struct net_device *dev)
{
	printk("astoncnet_init\n");

    ether_setup(dev);//填充一些以太网中的设备结构体的项
    
    /* keep the default flags, just add NOARP */
    dev->flags           |= IFF_NOARP;

//为priv分配内存
	dev->ml_priv = kmalloc(sizeof(struct astonnet_priv), GFP_KERNEL);
	if (dev->ml_priv == NULL)
		return -ENOMEM;
    memset(dev->ml_priv, 0, sizeof(struct astonnet_priv));
    spin_lock_init(&((struct astonnet_priv *)dev->ml_priv)->lock);
	
    return 0;
}


static const struct net_device_ops astonnet_netdev_ops = {
	.ndo_open		= astonnet_open,			// 打开网卡	对应 ifconfig xx up
	.ndo_stop		= astonnet_release,			// 关闭网卡	对应 ifconfig xx down
	.ndo_start_xmit	= astonnet_tx,				// 开启数据包传输
	.ndo_init       = astonnet_init,			// 初始化网卡硬件
};

static void	aston_plat_net_release(struct device *dev)
{
	printk("aston_plat_net_release\n");
}

static int __devinit aston_net_probe(struct platform_device *pdev)
{
	int result=0;

	astonnet_devs = alloc_etherdev(sizeof(struct net_device));
	astonnet_devs->netdev_ops = &astonnet_netdev_ops;

	strcpy(astonnet_devs->name, "astonnet0");
	if ((result = register_netdev(astonnet_devs)))
		printk("astonnet: error %i registering device \"%s\"\n", result,  astonnet_devs->name);
	
	return 0;
}

static int  __devexit  aston_net_remove(struct platform_device *pdev)   //设备移除接口
{
        kfree(astonnet_devs->ml_priv);
        unregister_netdev(astonnet_devs);
        return 0;
}

static struct platform_device aston_net= {
	.name	= "aston_net",
	.id		= -1,
    .dev	= {
		.release = aston_plat_net_release,
	},
};

static struct platform_driver aston_net_driver = {
    .probe  = aston_net_probe,                 	
    .remove  = __devexit_p(aston_net_remove),  	

	.driver  = {
		.name ="aston_net",
		.owner = THIS_MODULE,
	},
};

static int __init aston_net_init(void)
{
	printk("aston_net_init \n");
	
	platform_device_register(&aston_net);
	return platform_driver_register(&aston_net_driver );    	
}

static void __exit aston_net_cleanup(void)
{
	platform_driver_unregister(&aston_net_driver );    	
	platform_device_unregister(&aston_net);
}

module_init(aston_net_init);
module_exit(aston_net_cleanup);
MODULE_LICENSE("GPL");

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值