网络设备结构体net_device介绍
Structnet_devie结构体一、struct net_device结构体(1)简述 Linux系统为了支持各种硬件设备,又要屏蔽网络硬件与驱动程序的实现细节,而把网络设备抽象为struct net_device数据结构。 不同的厂商的网络设备配置器在物理层和数据链路层肯能由差别,这就需要网路驱动程序,linux网路体系中可有多个协议栈,这需要网络设备和上层协议栈之间有
·
Struct net_devie结构体
一、struct net_device结构体
(1)简述
Linux系统为了支持各种硬件设备,又要屏蔽网络硬件与驱动程序的实现细节,而把网络设备抽象为struct net_device数据结构。 不同的厂商的网络设备配置器在物理层和数据链路层肯能由差别,这就需要网路驱动程序,linux网路体系中可有多个协议栈,这需要网络设备和上层协议栈之间有一个统一接口,内核所在文件include/linux/netdevice.h
(2)struct net_device主要成员
(2.1)Name[IFNAMSIZ]
网络设备名字,网络设备的取名规则是字母加数字,字母表示网络设备的类型,数字表示系统中有多少个这样的网络设备,如eth0、eth1。
(2.2)name_hlist
以网络设备名字建立哈希链表,方便对网络设备的快速查找。
(2.3)irq
网络设备的中断号,设备驱动调用request_irq函数申请所需要的中断号,设备卸载时调用free_irq函数释放占用的中断。
(2.4)dev_list
网络设备的链表,系统中所有的网络设备多会加入到一个全局链表中,这个链表的基地址是dev_base_head指针变量。
(2.5)header_ops
Struct header_ops包含以一系列函数指针,主要是对链路层协议头的操作,比如协议头的创建、解析、重建协议头。
struct header_ops {
int (*create) (struct sk_buff *skb, struct net_device *dev, /*协议头的创建函数指针*/
unsigned short type, const void *daddr,
const void *saddr, unsigned len);
int (*parse)(const struct sk_buff *skb, unsigned char *haddr); /*协议头解析函数指针*/
int (*rebuild)(struct sk_buff *skb); /*协议头重构函数指针*/
#define HAVE_HEADER_CACHE
int (*cache)(const struct neighbour *neigh, struct hh_cache *hh);
void (*cache_update)(struct hh_cache *hh,
const struct net_device *dev,
const unsigned char *haddr);
};
(2.6) *_tx
网络协议栈把数据包放入设备的队列后调用网络设备驱动程序的函数hard_start_xmit来通知网络设备数据包已经准备好了可以发送了。一个网络设备可能有多个发送队列,根据队列优先级的不同调度不同的队列,由struct netdev_queue来管理队列,*_tx指向struct netdev_queue数组的首地址,
struct netdev_queue {
/*
* read mostly part
*/
struct net_device *dev; //队列所属的网络设备
struct Qdisc *qdisc; //队列操作函数
unsigned long state; //队列状态
struct Qdisc *qdisc_sleeping;
/*
* write mostly part
*/
spinlock_t _xmit_lock ____cacheline_aligned_in_smp;
int xmit_lock_owner;
/*
* please use this field instead of dev->trans_start
*/
unsigned long trans_start;
unsigned long tx_bytes; //该队列发送了多少字节
unsigned long tx_packets; //发送了多少数据包
unsigned long tx_dropped; //丢弃了多事数据包
} ____cacheline_aligned_in_smp;
(2.6.1)管理发送队列
u16 (*ndo_select_queue)(struct net_device *dev,struct sk_buff *skb)函数选择数据包发送队列,定义在struct net_device数据结构的回调函数中。设备驱动程序通过函数ndo_start_xmit从任意发送队列中得到发送的数据包,通过函数static inline u16 skb_get_queue_mapping(const struct sk_buff *skb)得到数据包所在队列(2.7)net_device_ops
最重要的成员出场,net_device_ops的成员都是函数指针,是网络驱动程序实现的功能函数,这里我们可以定义自己的网络设备的收包发包函数的细节。
struct net_device_ops {
int (*ndo_init)(struct net_device *dev); //提供网络设备的初始化、创建网络设备struct net_device 数据结构实例、初始化struct net_device的相关数据 域如设备名、i/o端口地址、中断号、向内核注册设备
void (*ndo_uninit)(struct net_device *dev); //注销设备的时候用
int (*ndo_open)(struct net_device *dev); //打开网络设备,主要注册的设备才能打开
int (*ndo_stop)(struct net_device *dev); //停止网络设备,注销的时候调用和open相反
netdev_tx_t (*ndo_start_xmit) (struct sk_buff *skb,
struct net_device *dev); //初始化数据包发送过程,初始化成功后数据包就放入网络适配器的发哦送你个数据缓冲区
u16(*ndo_select_queue)(struct net_device *dev,
struct sk_buff *skb); //当网络设备支持多个发送队列时用于选择发送队列
................
}
(2.8)*ethtool_ops
Struct ethtool_ops结构体的成员是一系列函数指针,在驱动程序中实现这些接口,主要用来修改和报告网络设备设置,比如:控制速度、介质类型、DMZ设置。
二、网络设备初始化
(2.1)为设备创建struct net_device数据结构实例
要创建struct net_device数据结构实例首先要申请内存空间,#define alloc_netdev(sizeof_priv, name, setup) \alloc_netdev_mq(sizeof_priv, name, setup, queue_count)最终调用alloc_netdev_mq
参数:
(1)、sizeof_priv:私有数据结构所需要的内存空间,alloc_netdev_mq为网络设备分配的空间又三大部分组成:
(2)、net_device结构体所需要的空间+私有数据结构所占用的内存空间+设备发送队列所站的内存空间
(3)、name:设备名字
(4)、*setup:函数指针,用来初始化struct net_device部分数据结构
(5)、queue_count:网络设备的发送队列数
*setup函数指针用来初始化net_device部分数据域,不同硬件的网络设备类型对应不同的名字,如以太网对应的名字是ether_setup,如下表所示
网络设备类型 | 包装函数名 | 包装函数定义 |
以太网 | alloc_etherdev_mq | return alloc_netdev_mq(sizeof_priv, "eth%d", ether_setup) |
光纤分布式接口 | alloc_fddidev_mq | return aloc_netdev_mq(sizeof_priv, "fddi%d", fddi_setup) |
令牌环(Token Ring) | alloc_irdadev_mq | return alloc_net_mq(sizeof_priv, "tr%d", tr_setup) |
void ether_setup(struct net_device *dev)
{
dev->header_ops = ð_header_ops; //k函数指针
dev->type = ARPHRD_ETHER;
dev->hard_header_len = ETH_HLEN;
dev->mtu = ETH_DATA_LEN; //mtu
dev->addr_len = ETH_ALEN;
dev->tx_queue_len = 1000; /* Ethernet wants good queues */
dev->flags = IFF_BROADCAST|IFF_MULTICAST;
memset(dev->broadcast, 0xFF, ETH_ALEN);
}
(2.2)驱动程序初始化struct net_device数据结构的其他数据域
在驱动程序中调用xxx_probe初始化struct net_device数据结构的其他数据域,包括初始化网络设备操作函数指针dev->netdev_ops、向系统申请中断号dev->irq、初始化网络设备的DMA通道dev->dma、申请struct net_device的私有空间
上代码:
static int __devinit e100_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct net_device *netdev;
struct nic *nic; /*网络设备私有空间*/
int err;
if (!(netdev = alloc_etherdev(sizeof(struct nic)))) {/*申请网络设备私有空间*/
if (((1 << debug) - 1) & NETIF_MSG_PROBE)
pr_err("Etherdev alloc failed, aborting\n");
return -ENOMEM;
}
netdev->netdev_ops = &e100_netdev_ops;/*网络设备操作函数初始化*/
SET_ETHTOOL_OPS(netdev, &e100_ethtool_ops);
netdev->watchdog_timeo = E100_WATCHDOG_PERIOD;
strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1); /*网络设备名字*/
nic = netdev_priv(netdev); /*得到私有空间地址*/
netif_napi_add(netdev, &nic->napi, e100_poll, E100_NAPI_WEIGHT); /*poll初始化*/
nic->netdev = netdev; /*指向网络设备结构体*/
nic->pdev = pdev;
nic->msg_enable = (1 << debug) - 1;
nic->mdio_ctrl = mdio_ctrl_hw;
pci_set_drvdata(pdev, netdev);
if ((err = pci_enable_device(pdev))) {
netif_err(nic, probe, nic->netdev, "Cannot enable PCI device, aborting\n");
goto err_out_free_dev;
}
if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
netif_err(nic, probe, nic->netdev, "Cannot find proper PCI device base address, aborting\n");
err = -ENODEV;
goto err_out_disable_pdev;
}
if ((err = pci_request_regions(pdev, DRV_NAME))) {
netif_err(nic, probe, nic->netdev, "Cannot obtain PCI resources, aborting\n");
goto err_out_disable_pdev;
}
if ((err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)))) { /*DMA通道初始化*/
netif_err(nic, probe, nic->netdev, "No usable DMA configuration, aborting\n");
goto err_out_free_res;
}
SET_NETDEV_DEV(netdev, &pdev->dev);
if (use_io)
netif_info(nic, probe, nic->netdev, "using i/o access mode\n");
nic->csr = pci_iomap(pdev, (use_io ? 1 : 0), sizeof(struct csr));
if (!nic->csr) {
netif_err(nic, probe, nic->netdev, "Cannot map device registers, aborting\n");
err = -ENOMEM;
goto err_out_free_res;
}
if (ent->driver_data)
nic->flags |= ich;
else
nic->flags &= ~ich;
e100_get_defaults(nic);
/* locks must be initialized before calling hw_reset */
spin_lock_init(&nic->cb_lock);
spin_lock_init(&nic->cmd_lock);
spin_lock_init(&nic->mdio_lock);
/* Reset the device before pci_set_master() in case device is in some
* funky state and has an interrupt pending - hint: we don't have the
* interrupt handler registered yet. */
e100_hw_reset(nic);
pci_set_master(pdev);
init_timer(&nic->watchdog);
nic->watchdog.function = e100_watchdog; /*watchdog初始化*/
nic->watchdog.data = (unsigned long)nic;
init_timer(&nic->blink_timer);
nic->blink_timer.function = e100_blink_led;
nic->blink_timer.data = (unsigned long)nic;
INIT_WORK(&nic->tx_timeout_task, e100_tx_timeout_task); /*超时时间初始化*/
if ((err = e100_alloc(nic))) {
netif_err(nic, probe, nic->netdev, "Cannot alloc driver memory, aborting\n");
goto err_out_iounmap;
}
if ((err = e100_eeprom_load(nic)))
goto err_out_free;
e100_phy_init(nic);
memcpy(netdev->dev_addr, nic->eeprom, ETH_ALEN);
memcpy(netdev->perm_addr, nic->eeprom, ETH_ALEN);
if (!is_valid_ether_addr(netdev->perm_addr)) {
if (!eeprom_bad_csum_allow) {
netif_err(nic, probe, nic->netdev, "Invalid MAC address from EEPROM, aborting\n");
err = -EAGAIN;
goto err_out_free;
} else {
netif_err(nic, probe, nic->netdev, "Invalid MAC address from EEPROM, you MUST configure one.\n");
}
}
/* Wol magic packet can be enabled from eeprom */
if ((nic->mac >= mac_82558_D101_A4) &&
(nic->eeprom[eeprom_id] & eeprom_id_wol)) {
nic->flags |= wol_magic;
device_set_wakeup_enable(&pdev->dev, true);
}
/* ack any pending wake events, disable PME */
pci_pme_active(pdev, false);
strcpy(netdev->name, "eth%d");
if ((err = register_netdev(netdev))) { /*注册网络设备*/
netif_err(nic, probe, nic->netdev, "Cannot register net device, aborting\n");
goto err_out_free;
}
nic->cbs_pool = pci_pool_create(netdev->name,
nic->pdev,
nic->params.cbs.max * sizeof(struct cb),
sizeof(u32),
0);
netif_info(nic, probe, nic->netdev,
"addr 0x%llx, irq %d, MAC addr %pM\n",
(unsigned long long)pci_resource_start(pdev, use_io ? 1 : 0),
pdev->irq, netdev->dev_addr); /*向系统申请软中断*/
return 0;
err_out_free:
e100_free(nic);
err_out_iounmap:
pci_iounmap(pdev, nic->csr);
err_out_free_res:
pci_release_regions(pdev);
err_out_disable_pdev:
pci_disable_device(pdev);
err_out_free_dev:
pci_set_drvdata(pdev, NULL);
free_netdev(netdev);
return err;
}
更多推荐
已为社区贡献2条内容
所有评论(0)