linux系统网卡驱动编码实例

2022-06-05 00:08:18 浏览数 (3)

通过register_netdev注册网络设备。

实现了网络中断。

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

检查ip数据包。

记录传输的数据包状态。

手动释放sk_buffer。

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

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

代码

demo.c

代码语言:javascript复制
#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 packetn");
        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("netopenn");
    ret = request_irq(IRQ_NET_CHIP, net_interrupt, IRQF_SHARED, dev->name, dev);
    if (ret) return ret;
    printk("request_irq okn");
    netif_start_queue(dev);
    return 0;
}
 
int netrelease(struct net_device *dev)
{
    printk("netreleasen");
    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_buffe
    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文件

代码语言:javascript复制
#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

代码语言:javascript复制
#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

0 人点赞