linux系统网卡驱动编码实例

通过register_netdev注册网络设备。

实现了网络中断。

模拟网络硬件传输数据包。

检查ip数据包。

记录传输的数据包状态。

手动释放sk_buffer。

处理数据包传输超时情况。

网络驱动接收网络数据包并将数据包放入TCP/IP上层,编写网络驱动接收数据包必须分配sk_buff结构来存储数据,sk_buff将在上层释放。

代码

demo.c 

#include <linux/module.h>
#include <linux/init.h>
#include <linux/moduleparam.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>	
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/seq_file.h>
#include <linux/cdev.h>
#include <linux/version.h>
#include <linux/vmalloc.h>
#include <linux/delay.h>
#include <linux/ctype.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/slab.h>      // kmalloc()
#include <linux/types.h>     // size_t
#include <linux/interrupt.h> // mark_bh

#include <linux/in.h>
#include <linux/netdevice.h>   
#include <linux/etherdevice.h> 
#include <linux/ip.h>          
#include <linux/tcp.h>
#include <linux/skbuff.h>

#include <linux/in6.h>
#include <asm/checksum.h>

#include "demo.h"

#define IRQ_NET_CHIP  200

MODULE_AUTHOR("sun");
MODULE_LICENSE("Dual BSD/GPL");

static char  netbuffer[100];
struct net_device *net_devs;

// 接收网络数据包并将数据包放入TCP/IP上层 netif_rx主要实现该功能
void netrx(struct net_device *dev, int len, unsigned char *buf)
{
    struct sk_buff *skb;
    struct netpriv *priv = (struct netpriv *) dev->priv;

    skb = dev_alloc_skb(len+2);
    if (!skb) {
        printk("netrx can not allocate more memory to store the packet. drop the packet\n");
        priv->stats.rx_dropped++;
        return;
    }

    skb_reserve(skb, 2);
    memcpy(skb_put(skb, len), buf, len);

    skb->dev = dev;
    skb->protocol = eth_type_trans(skb, dev);

    // 不需要检查checksum
    skb->ip_summed = CHECKSUM_UNNECESSARY; 
    priv->stats.rx_packets++;
                              
    netif_rx(skb);
    return;
}

static irqreturn_t net_interrupt (int irq, void *dev_id)
{
	struct net_device *dev;
	dev = (struct net_device *) dev_id;

    // 从硬件寄存器获取数据

	netrx(dev, 100, netbuffer);
	
	return IRQ_HANDLED;
}

int netopen(struct net_device *dev)
{
	int ret=0;
	printk("netopen\n");
    ret = request_irq(IRQ_NET_CHIP, net_interrupt, IRQF_SHARED, dev->name, dev);
	if (ret) return ret;
    printk("request_irq ok\n");
	netif_start_queue(dev);
    return 0;
}

int netrelease(struct net_device *dev)
{
	printk("netrelease\n");
    netif_stop_queue(dev);          
    return 0;
}

// 模拟网络硬件传输 把数据input给ed_tx设备即可
void nethw_tx(char *buf, int len, struct net_device *dev)
{
    struct netpriv *priv;
   
    // 检查ip数据包长度
    if (len < sizeof(struct ethhdr) + sizeof(struct iphdr))
	{
        printk("packet's size is less then 34!\n");
        return;
    }

    // 记录传输的数据包状态
    priv = (struct netpriv *) dev->priv;
    priv->stats.tx_packets++;
    priv->stats.rx_bytes += len;
    
    // 释放分配的sk_buffer
    dev_kfree_skb(priv->skb);
}

// 传输数据包 内核会调用这个函数
int nettx(struct sk_buff *skb, struct net_device *dev)
{
    int len;
    char *data;
    struct netpriv *priv = (struct netpriv *) dev->priv;

    len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len;
    data = skb->data;

    // 时间戳
    dev->trans_start = jiffies;

    // 这个sk_buffer 在nethw_tx 释放
    priv->skb = skb;
    
    /* pseudo transmit the packet,hehe */
    nethw_tx(data, len, dev);

    return 0; 
}

// 处理数据包传输超时情况
void nettx_timeout (struct net_device *dev)
{
    struct netpriv *priv = (struct netpriv *) dev->priv;
    priv->stats.tx_errors++;
    netif_wake_queue(dev);

    return;
}


int netioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
    return 0;
}

// ifconfig获取数据包传输状态
struct net_device_stats *netstats(struct net_device *dev)
{
    struct netpriv *priv = (struct netpriv *) dev->priv;
    return &priv->stats;
}

// tcp/ip 握手会调用这个函数
int netchange_mtu(struct net_device *dev, int new_mtu)
{
    unsigned long flags;
    spinlock_t *lock = &((struct netpriv *) dev->priv)->lock;
    
    // mtu最大传输单元 不能小于68 大于1500
    if (new_mtu < 68)
        return -EINVAL;
   
    spin_lock_irqsave(lock, flags);
    dev->mtu = new_mtu;
    spin_unlock_irqrestore(lock, flags);

    return 0; 
}

void netinit(struct net_device *dev)
{
	struct netpriv *priv;
	ether_setup(dev);

	dev->open            = netopen;
	dev->stop            = netrelease;
	dev->hard_start_xmit = nettx;
	dev->do_ioctl        = netioctl;
	dev->get_stats       = netstats;
	dev->change_mtu      = netchange_mtu;  
	dev->tx_timeout      = nettx_timeout;

	dev->dev_addr[0] = 0x18;//(0x01 & addr[0])为multicast
	dev->dev_addr[1] = 0x02;
	dev->dev_addr[2] = 0x03;
	dev->dev_addr[3] = 0x04;
	dev->dev_addr[4] = 0x05;
	dev->dev_addr[5] = 0x06;

    // 网卡标准接口标志位 
	dev->flags           |= IFF_NOARP;
	dev->features        |= NETIF_F_NO_CSUM;

    // 初始化priv 包含了统计数据和一些私有字段
	priv = netdev_priv(dev);
	memset(priv, 0, sizeof(struct netpriv));
	spin_lock_init(&priv->lock);
}

void net_module_cleanup(void)
{
	if (net_devs) 
	{
		unregister_netdev(net_devs);
		free_netdev(net_devs);
	}
	return;
}

int net_module_init(void)
{
	int result,ret = -ENOMEM;

	// allocate devices
	net_devs=alloc_netdev(sizeof(struct netpriv), "eth%d", netinit);
	if (net_devs == NULL)
		goto out;

	ret = -ENODEV;
	if ((result = register_netdev(net_devs)))
		printk("demo: error %i registering device \"%s\"\n", result, net_devs->name);
	else
		ret = 0;
   out:
	if (ret) 
		net_module_cleanup();
	return ret;
}

module_init(net_module_init);
module_exit(net_module_cleanup);

网卡标准接口标记位

/usr/include/net/if.h文件

#ifdef __USE_MISC
/* Standard interface flags. */
enum
  {
    IFF_UP = 0x1,               /* Interface is up.  */
# define IFF_UP IFF_UP
    IFF_BROADCAST = 0x2,        /* Broadcast address valid.  */
# define IFF_BROADCAST  IFF_BROADCAST
    IFF_DEBUG = 0x4,            /* Turn on debugging.  */
# define IFF_DEBUG      IFF_DEBUG
    IFF_LOOPBACK = 0x8,         /* Is a loopback net.  */
# define IFF_LOOPBACK   IFF_LOOPBACK
    IFF_POINTOPOINT = 0x10,     /* Interface is point-to-point link.  */
# define IFF_POINTOPOINT IFF_POINTOPOINT
    IFF_NOTRAILERS = 0x20,      /* Avoid use of trailers.  */
# define IFF_NOTRAILERS IFF_NOTRAILERS
    IFF_RUNNING = 0x40,         /* Resources allocated.  */
# define IFF_RUNNING    IFF_RUNNING
    IFF_NOARP = 0x80,           /* No address resolution protocol.  */
# define IFF_NOARP      IFF_NOARP
    IFF_PROMISC = 0x100,        /* Receive all packets.  */
# define IFF_PROMISC    IFF_PROMISC

    /* Not supported */
    IFF_ALLMULTI = 0x200,       /* Receive all multicast packets.  */
# define IFF_ALLMULTI   IFF_ALLMULTI

    IFF_MASTER = 0x400,         /* Master of a load balancer.  */
# define IFF_MASTER     IFF_MASTER
    IFF_SLAVE = 0x800,          /* Slave of a load balancer.  */
# define IFF_SLAVE      IFF_SLAVE

    IFF_MULTICAST = 0x1000,     /* Supports multicast.  */
# define IFF_MULTICAST  IFF_MULTICAST

    IFF_PORTSEL = 0x2000,       /* Can set media type.  */
# define IFF_PORTSEL    IFF_PORTSEL
    IFF_AUTOMEDIA = 0x4000,     /* Auto media select active.  */
# define IFF_AUTOMEDIA  IFF_AUTOMEDIA
    IFF_DYNAMIC = 0x8000        /* Dialup device with changing addresses.  */
# define IFF_DYNAMIC    IFF_DYNAMIC
  };

代码头文件

demo.h


#ifndef _ED_DEVICE_H
#define _ED_DEVICE_H

#define ED_REC_DEVICE      0
#define ED_TX_DEVICE       1

// 定义字符设备名
#define ED_REC_DEVICE_NAME  "ed_rec"
#define ED_TX_DEVICE_NAME   "ed_tx"

#define ED_MTU              192
#define ED_MAGIC	     0x999
#define BUFFER_SIZE	     2048

#define MAJOR_NUM_REC 200
#define MAJOR_NUM_TX  201
#define IOCTL_SET_BUSY _IOWR(MAJOR_NUM_TX,1,int)
#include <linux/netdevice.h>

struct ed_device{
	int magic;
	char name[8]; 	
	int busy;
	unsigned char *buffer;
    wait_queue_head_t rwait;
	int mtu;
	spinlock_t lock;
	int tx_len;
    int rx_len;
    int buffer_size;
	struct file *file;
    ssize_t (*kernel_write)(const char *buffer,size_t length,int buffer_size);
};

// 自定义的数据结构
struct netpriv
{
    struct net_device_stats stats;

    struct sk_buff *skb;
    spinlock_t lock;
};

#define ED_TIMEOUT 5

#endif
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值