网卡驱动的队列数据的接收e100

网卡驱动和队列层中的数据包接收

 

一、从网卡说起

 

 

这并非是一个网卡驱动分析的专门文档,只是对网卡处理数据包的流程进行一个重点的分

析。这里以

Intel

e100

驱动为例进行分析。

 

大多数网卡都是一个

PCI

设备,

PCI

设备都包含了一个标准的配置寄存器,寄存器中,包含

PCI

设备的厂商

ID

、设备

ID

等等信息,驱动

 

程序使用来描述这些寄存器的标识符。如下:

 

 

struct pci_device_id { 

  

  

  

  

__u32 vendor, device;  

  

  

  

  

  

  

  

/* Vendor and device ID or PCI_ANY_ID*/ 

  

  

  

  

__u32 subvendor, subdevice;  

  

  

  

/* Subsystem ID's or PCI_ANY_ID */ 

  

  

  

  

__u32 class, class_mask;  

  

  

  

/* (class,subclass,prog-if) triplet */ 

  

  

  

  

kernel_ulong_t driver_data;  

  

  

  

/* Data private to the driver */ 

}; 

 

这样,在驱动程序中,常常就可以看到定义一个

struct pci_device_id 

类型的数组,告诉内核

支持不同类型的

 

PCI

设备的列表,以

e100

驱动为例:

 

 

  

 

#define INTEL_8255X_ETHERNET_DEVICE(device_id, ich) {\ 

  

  

  

  

PCI_VENDOR_ID_INTEL, device_id, PCI_ANY_ID, PCI_ANY_ID, \ 

  

  

  

  

PCI_CLASS_NETWORK_ETHERNET << 8, 0xFFFF00, ich } 

  

  

  

   

static struct pci_device_id e100_id_table[] = { 

  

  

  

  

INTEL_8255X_ETHERNET_DEVICE(0x1029, 0), 

  

  

  

  

INTEL_8255X_ETHERNET_DEVICE(0x1030, 0), 

  

  

  

  

INTEL_8255X_ETHERNET_DEVICE(0x1031, 3), 

„„

/*

略过一大堆支持的设备

*/ 

  

  

  

  

{ 0, } 

}; 

 

在内核中,一个

PCI

设备,使用

struct pci_driver

结构来描述,

 

struct pci_driver { 

  

  

  

  

struct list_head node; 

  

  

  

  

char *name; 

  

  

  

  

struct module *owner; 

  

  

  

  

const struct pci_device_id *id_table;  

  

  

  

/* must be non-NULL for probe to be called */ 

  

  

  

  

int  (*probe)  (struct 

pci_dev 

*dev, 

const 

struct 

pci_device_id 

*id);  

  

  

  

/* 

New 

device 

inserted */ 

  

  

  

  

void (*remove) (struct pci_dev *dev);  

  

  

  

/* Device removed (NULL if not a hot-plug 

capable driver) */ 

  

  

  

  

int  (*suspend) (struct pci_dev *dev, pm_message_t state);  

  

  

  

/* Device suspended */ 

  

  

  

  

int  (*resume) (struct pci_dev *dev);  

  

  

   

   

   

   

   

   /* Device woken up */ 

  

  

  

  

int  (*enable_wake) (struct pci_dev *dev, pci_power_t state, int enable);  

 /* Enable wake 

event */ 

  

  

  

  

void (*shutdown) (struct pci_dev *dev); 

 

  

  

  

  

struct device_driver  

  

  

  

driver; 

  

  

  

  

struct pci_dynids dynids; 

}; 

 

因为在系统引导的时候,

PCI

设备已经被识别,

当内核发现一个已经检测到的设备同驱动注

册的

id_table

中的信息相匹配时,

 

它就会触发驱动的

probe

函数,以

e100

为例:

 

/* 

定义一个名为

e100_driver

PCI

设备

 

* 1

、设备的探测函数为

e100_probe; 

* 2

、设备的

id_table

表为

e100_id_table 

*/ 

static struct pci_driver e100_driver = { 

  

  

  

  .name =  

   

   

 DRV_NAME, 

  

  

  

  .id_table =  

   e100_id_table, 

  

  

  

  .probe =  

   

   e100_probe, 

  

  

  

  .remove =  

   

  

__devexit_p(e100_remove), 

#ifdef CONFIG_PM 

  

  

  

  .suspend =  

   

 e100_suspend, 

  

  

  

  .resume =  

   

  

e100_resume, 

#endif 

 

  

  

  

  .driver = { 

  

  

  

  

  

  

  

  

.shutdown = e100_shutdown, 

  

  

  

  

 

}; 

 

这样,如果系统检测到有与

id_table

中对应的设备时,就调用驱动的

probe

函数。

 

 

驱动设备在

init

函数中,调用

pci_module_init

函数初始化

PCI

设备

e100_driver: 

 

static int __init e100_init_module(void) 

  

  

  

  

if(((1 << debug) - 1) & NETIF_MSG_DRV) { 

  

  

  

  

  

  

  

  

printk(KERN_INFO PFX "%s, %s\n", DRV_DESCRIPTION, DRV_VERSION); 

  

  

  

  

  

  

  

  

printk(KERN_INFO PFX "%s\n", DRV_COPYRIGHT); 

  

  

  

  

  

  

  

  

return pci_module_init(&e100_driver); 

 

一切顺利的话,注册的

e100_probe

函数将被内核调用,这个函数完成两个重要的工作:

 

1

、分配

/

初始化

/

注册网络设备;

 

2

、完成

PCI

设备的

I/O

区域的分配和映射,以及完成硬件的其它初始化工作;

 

 

网络设备使用

struct 

net_device

结构来描述,这个结构非常之大,许多重要的参考书籍对它

都有较为深入的描述,可以参考《

Linux

设备驱动程序》中网卡驱动设计的相关章节。我会

在后面的内容中,对其重要的成员进行注释;

 

 

probe

函数被调用,

证明已经发现了我们所支持的网卡,

这样,

就可以调用

register_netdev

函数向内核注册网络设备了,注册之前,一般会调用

alloc_etherdev

为以太网分析一个

net_device

,然后初始化它的重要成员。

 

 

除了向内核注册网络设备之外,

探测函数另一项重要的工作就是需要对硬件进行初始化,

如,要访问其

I/O

区域,需要为

I/O

区域分配内存区域,然后进行映射,这一步一般的流程

是:

 

1

request_mem_region() 

2

ioremap() 

 

对于一般的

PCI

设备而言,可以调用:

 

1

pci_request_regions() 

2

ioremap() 

 

pci_request_regions

函数对

PCI

6

个寄存器都会调用资源分配函数进行申请(需要判断是

I/O

端口还是

I/O

内存)

,例如:

 

 

int pci_request_regions(struct pci_dev *pdev, char *res_name) 

  

  

  

  

int i; 

  

  

  

   

  

  

  

  

for (i = 0; i < 6; i++) 

  

  

  

  

  

  

  

  

if(pci_request_region(pdev, i, res_name)) 

  

  

  

  

  

  

  

  

  

  

  

  

goto err_out; 

  

  

  

  

return 0; 

 

 

int pci_request_region(struct pci_dev *pdev, int bar, char *res_name) 

  

  

  

  

if (pci_resource_len(pdev, bar) == 0) 

  

  

  

  

  

  

  

  

return 0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值