虚拟网卡驱动
alloc_netdev
#define alloc_netdev(sizeof_priv, name, setup) \
alloc_netdev_mq(sizeof_priv, name, setup, 1)
/**
* alloc_netdev_mq - allocate network device
* @sizeof_priv: size of private data to allocate space for
* @name: device name format string
* @setup: callback to initialize device
* @queue_count: the number of subqueues to allocate
*
* Allocates a struct net_device with private data area for driver use
* and performs basic initialization. Also allocates subquue structs
* for each queue on the device at the end of the netdevice.
*/
struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name,
void (*setup)(struct net_device *), unsigned int queue_count)
{
struct netdev_queue *tx;
struct net_device *dev;
size_t alloc_size;
struct net_device *p;
BUG_ON(strlen(name) >= sizeof(dev->name));
alloc_size = sizeof(struct net_device);
if (sizeof_priv) {
/* ensure 32-byte alignment of private area */
alloc_size = ALIGN(alloc_size, NETDEV_ALIGN);
alloc_size += sizeof_priv;
}
/* ensure 32-byte alignment of whole construct */
alloc_size += NETDEV_ALIGN - 1;
p = kzalloc(alloc_size, GFP_KERNEL);
if (!p) {
printk(KERN_ERR "alloc_netdev: Unable to allocate device.\n");
return NULL;
}
tx = kcalloc(queue_count, sizeof(struct netdev_queue), GFP_KERNEL);
if (!tx) {
printk(KERN_ERR "alloc_netdev: Unable to allocate "
"tx qdiscs.\n");
goto free_p;
}
dev = PTR_ALIGN(p, NETDEV_ALIGN);
dev->padded = (char *)dev - (char *)p;
if (dev_addr_init(dev))
goto free_tx;
dev_unicast_init(dev);
dev_net_set(dev, &init_net);
dev->_tx = tx;
dev->num_tx_queues = queue_count;
dev->real_num_tx_queues = queue_count;
dev->gso_max_size = GSO_MAX_SIZE;
netdev_init_queues(dev);
INIT_LIST_HEAD(&dev->napi_list);
dev->priv_flags = IFF_XMIT_DST_RELEASE;
setup(dev);
strcpy(dev->name, name);
return dev;
free_tx:
kfree(tx);
free_p:
kfree(p);
return NULL;
}
EXPORT_SYMBOL(alloc_netdev_mq);
sizeof_priv:私有数据大小
name:设备名
setup:net_device部分成员初始化
alloc_etherdev
#define alloc_etherdev(sizeof_priv) alloc_etherdev_mq(sizeof_priv, 1)
struct net_device *alloc_etherdev_mq(int sizeof_priv, unsigned int queue_count)
{
return alloc_netdev_mq(sizeof_priv, "eth%d", ether_setup, queue_count);
}
alloc_etherdev相比alloc_netdev更常用
网络接口的打开与关闭
static inline void netif_start_queue(struct net_device *dev)
static inline void netif_stop_queue(struct net_device *dev)
netdev_priv
#define netdev_priv(dev) (dev->priv)
存取私有数据指针
dev_hold
设备的引用计数器加1
dev_put
设备的引用计数器减1
dev_get_by_name
取得设备指针
记得dev_put取消设备引用
/**
* dev_get_by_name - find a device by its name
* @net: the applicable net namespace
* @name: name to find
*
* Find an interface by name. This can be called from any
* context and does its own locking. The returned handle has
* the usage count incremented and the caller must use dev_put() to
* release it when it is no longer needed. %NULL is returned if no
* matching device is found.
*/
struct net_device *dev_get_by_name(struct net *net, const char *name)
{
struct net_device *dev;
read_lock(&dev_base_lock);
dev = __dev_get_by_name(net, name);
if (dev)
dev_hold(dev);
read_unlock(&dev_base_lock);
return dev;
}
举例
#include <linux/init.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/ethtool.h>
static struct net_device *dev = NULL;
static int net_open(struct net_device *dev)
{
netif_start_queue(dev);
return 0;
}
static int net_start_xmit(struct sk_buf *skb, struct net_device *dev)
{
netif_start_queue(dev);
dev->stats.tx_packets++;
dev->stats.tx_bytes += skb->len;
/* this is normally done in the interrupt when tx finishes */
// netif_wake_queue(dev);
netif_stop_queue(dev);
return 0;
}
static int net_close(struct net_device *dev)
{
netif_stop_queue(dev);
return 0;
}
static int __init net_init(void)
{
dev = alloc_etherdev(0);
dev->open = net_open;
dev->hard_start_xmit = &net_start_xmit;
dev->stop = net_close;
dev->tx_timeout = net_tx_timeout;
ether_setup(dev);
//注册网卡设备
register_netdev(dev);
return 0;
}
//出口函数
static void __exit net_exit(void)
{
unregister_netdev(dev);
free_netdev(dev);
return;
}
module_init(net_init);
module_exit(net_exit);
启动网卡
# ifconfig "eth%d" 192.168.1.1 up