err = dma_set_mask(pci_dev_to_dev(pdev), DMA_BIT_MASK(64));
if (!err) {
err =
dma_set_coherent_mask(pci_dev_to_dev(pdev),
DMA_BIT_MASK(64));
if (!err)
pci_using_dac = 1;
} else {
err = dma_set_mask(pci_dev_to_dev(pdev), DMA_BIT_MASK(32));
if (err) {
err = dma_set_coherent_mask(pci_dev_to_dev(pdev),
DMA_BIT_MASK(32));
if (err) {
dev_err(pci_dev_to_dev(pdev),
"No usable DMA configuration, aborting\n");
goto err_dma;
}
}
}
request_region(start,n,name)
inb(),outb()
inw(),outw()
inl(),outl()
如果设备需要IO内存,根据设备需要的IO内存大小,分配IO内存:pci_request_selected_regions(pdev, bars, name);
ioremap(mmio_start, mmio_len);
$ lspci | grep "Ethernet controller"
02:00.0 Ethernet controller: Intel Corporation 82574L Gigabit Network Connection
06:00.0 Ethernet controller: Intel Corporation 82574L Gigabit Network Connection
$ cat /proc/iomem | grep "02:00.0"
fea00000-fea1ffff : 0000:02:00.0
fea20000-fea23fff : 0000:02:00.0
mmio_start = pci_resource_start(pdev, 0);
mmio_len = pci_resource_len(pdev, 0);
err = -EIO;
adapter->hw.hw_addr = ioremap(mmio_start, mmio_len);
BAR1用来映射flash,pci_resource_start(pdev, 1)用来获取BAR1的起始地址:
if ((adapter->flags & FLAG_HAS_FLASH) &&
(pci_resource_flags(pdev, 1) & IORESOURCE_MEM)) {
flash_start = pci_resource_start(pdev, 1);
flash_len = pci_resource_len(pdev, 1);
adapter->hw.flash_address = ioremap(flash_start, flash_len);
if (!adapter->hw.flash_address)
goto err_flashmap;
}
netdev = alloc_etherdev(sizeof(struct e1000_adapter));
struct net_device *alloc_etherdev(int sizeof_priv)
{
return alloc_netdev(sizeof_priv, "eth%d", ether_setup);
}
该函数分配net_device结构,同时分配网卡的私有数据e1000_adapter,使用函数netdev_priv(netdev)获取网卡私有数据;网卡设备名为ethx,该函数分配有关数据结构后,会调用ether_setup初始化net_device一些成员,这是一个共用的函数,以太网卡驱动都会使用这个函数来初始化以太网网卡设备:
void ether_setup(struct net_device *dev)
{
dev->change_mtu = eth_change_mtu;
dev->hard_header = eth_header;
dev->rebuild_header = eth_rebuild_header;
dev->set_mac_address = eth_mac_addr;
dev->hard_header_cache = eth_header_cache;
dev->header_cache_update= eth_header_cache_update;
dev->hard_header_parse = eth_header_parse;
dev->type = ARPHRD_ETHER;
dev->hard_header_len = ETH_HLEN;
dev->mtu = ETH_DATA_LEN;
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);
}
Network device type |
Wrapper name |
Wrapper definition |
---|---|---|
Ethernet |
alloc_etherdev |
return alloc_netdev(sizeof_priv, "eth%d", ether_setup);//以太网 |
Fiber Distributed Data Interface |
alloc_fddidev |
return alloc_netdev(sizeof_priv, "fddi%d", fddi_setup); |
High Performace Parallel Interface |
alloc_hippi_dev |
return alloc_netdev(sizeof_priv, "hip%d", hippi_setup); |
Token Ring |
alloc_trdev |
return alloc_netdev(sizeof_priv, "tr%d", tr_setup);//令牌环网络 |
Fibre Channel |
alloc_fcdev |
return alloc_netdev(sizeof_priv, "fc%d", fc_setup);//光纤 |
Infrared Data Association |
alloc_irdadev |
return alloc_netdev(sizeof_priv, "irda%d", irda_device_setup); |
netdev->open = &e1000_open;
netdev->stop = &e1000_close;
netdev->hard_start_xmit = &e1000_xmit_frame;
enum netdev_state_t
{
__LINK_STATE_XOFF=0,
__LINK_STATE_START,
__LINK_STATE_PRESENT,
__LINK_STATE_SCHED,
__LINK_STATE_NOCARRIER,
__LINK_STATE_RX_SCHED,
__LINK_STATE_LINKWATCH_PENDING,
__LINK_STATE_DORMANT,
__LINK_STATE_QDISC_RUNNING,
__LINK_STATE_NETPOLL
};
比如,调用netif_stop_queue来停止队列:
static inline void netif_stop_queue(struct net_device *dev)
{
...
set_bit(_ _LINK_STATE_XOFF, &dev->state);
}
enum {
NETREG_UNINITIALIZED=0,
NETREG_REGISTERED, /* completed register_netdevice */
NETREG_UNREGISTERING, /* called unregister_netdevice */
NETREG_UNREGISTERED, /* completed unregister todo */
NETREG_RELEASED, /* called free_netdev */
} reg_state;
#define IFF_UP 0x1 /* interface is up */
#define IFF_BROADCAST 0x2 /* broadcast address valid */
#define IFF_DEBUG 0x4 /* turn on debugging */
#define IFF_LOOPBACK 0x8 /* is a loopback net */
#define IFF_POINTOPOINT 0x10 /* interface is has p-p link */
#define IFF_NOTRAILERS 0x20 /* avoid use of trailers */
#define IFF_RUNNING 0x40 /* interface RFC2863 OPER_UP */
#define IFF_NOARP 0x80 /* no ARP protocol */
#define IFF_PROMISC 0x100 /* receive all packets */
#define IFF_ALLMULTI 0x200 /* receive all multicast packets*/
$ ifconfig eth0
eth0 Link encap:Ethernet HWaddr 00:0C:29:C5:9C:3F
inet addr:172.16.252.202 Bcast:172.16.255.255 Mask:255.255.0.0
inet6 addr: fe80::20c:29ff:fec5:9c3f/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:40631942 errors:0 dropped:0 overruns:0 frame:0
TX packets:288276 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:2702596852 (2.5 GiB) TX bytes:248532391 (237.0 MiB)
unsigned long features;
#define NETIF_F_SG 1 /* Scatter/gather IO. */
#define NETIF_F_IP_CSUM 2 /* Can checksum only TCP/UDP over IPv4. */
#define NETIF_F_NO_CSUM 4 /* Does not require checksum. F.e. loopack. */
#define NETIF_F_HW_CSUM 8 /* Can checksum all the packets. */
#define NETIF_F_HIGHDMA 32 /* Can DMA to high memory. */
#define NETIF_F_FRAGLIST 64 /* Scatter/gather IO. */
#define NETIF_F_HW_VLAN_TX 128 /* Transmit VLAN hw acceleration */
#define NETIF_F_HW_VLAN_RX 256 /* Receive VLAN hw acceleration */
#define NETIF_F_HW_VLAN_FILTER 512 /* Receive filtering on VLAN */
#define NETIF_F_VLAN_CHALLENGED 1024 /* Device cannot handle VLAN packets */
adapter->rx_ring->count = 256;
adapter->tx_ring->count = 256;
关于描述符环下一篇会讲到。
err = register_netdev(netdev);
struct net_device *dev_base;
通过该指针,内核可以很方便的遍历所有的网络设备,不管是1Gb速率的网卡还是10Gb的网卡,如果需要获取某个网卡的数据或者修改某个网卡的配置,可以很方便的查找到该设备。
static struct hlist_head dev_name_head[1<
上面两个变量是长度为256的链表数组,可以保存256个链表。dev_name_head是根据设备的名称(比如“eth0”)生存的哈希值组成的链表,dev_index_head是根据分配给设备唯一的ID值组成的链表,该ID值保存在net_device中的ifindex成员中。
static inline struct hlist_head *dev_name_hash(const char *name)
{
unsigned hash = full_name_hash(name, strnlen(name, IFNAMSIZ));
return &dev_name_head[hash & ((1<
设备的ID值,即ifindex,是一个int类型的数据:
static int dev_new_index(void)
{
static int ifindex;
for (;;) {
if (++ifindex <= 0)
ifindex = 1;
if (!__dev_get_by_index(ifindex))
return ifindex;
}
}
在net_device有两个链表节点:
struct hlist_node name_hlist;//设备名链表节点
struct hlist_node index_hlist;//ID值链表节点
在register_netdevice函数中,会根据设备名生存的哈希值和设备ID值,找到256个链表中对应的链表,把上面两个链表节点加入到对应的链表中。整个链表是一个拉链型的链表。dev_get_by_index():
struct net_device *__dev_get_by_index(int ifindex)
{
struct hlist_node *p;
hlist_for_each(p, dev_index_hash(ifindex)) {
struct net_device *dev
= hlist_entry(p, struct net_device, index_hlist);
if (dev->ifindex == ifindex)
return dev;
}
return NULL;
}
dev_get_by_name():
struct net_device *__dev_get_by_name(const char *name)
{
struct hlist_node *p;
hlist_for_each(p, dev_name_hash(name)) {
struct net_device *dev
= hlist_entry(p, struct net_device, name_hlist);
if (!strncmp(dev->name, name, IFNAMSIZ))
return dev;
}
return NULL;
}
ifup eth0
register_netdevice_notifier(&rtnetlink_dev_notifier);
raw_notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev);
通知rtnetlink模块。