linux驱动架构变化,Linux网卡驱动架构分析

一、网卡驱动架构

由上到下层次依次为:应用程序→系统调用接口→协议无关接口→网络协议栈→设备无关接口→设备驱动。

二、重要数据结构

1、Linux内核中每一个网卡由一个net_device结构来描述。

2、网卡操作函数集:net_device_ops,这个数据结构是上面net_device的一个成员。

3、网络数据包:sk_buff。

三、网卡驱动代码分析

所用文件为cs89x0.c,主要分析三个部分:网卡初始化、发送数据、接收数据。

㈠网卡初始化

网卡驱动初始化主要在函数init_module中完成,部分代码如下:

int __init init_module(void)

{struct net_device *dev = alloc_etherdev(sizeof(structnet_local));struct net_local *lp;int ret = 0;

...

dev->irq = irq;

dev->base_addr = io;

...

ret = cs89x0_probe1(dev, io, 1);

...

}

cs89x0_probe1函数部分代码如下:

static int__init cs89x0_probe1(struct net_device *dev, int ioaddr, intmodular)

{struct net_local *lp =netdev_priv(dev);staticunsigned version_printed;inti;inttmp;

unsigned rev_type= 0;inteeprom_buff[CHKSUM_LEN];int retval;

...

writeword(ioaddr, ADD_PORT, PP_ChipID);

tmp = readword(ioaddr, DATA_PORT);      //对硬件的初始化

...

for (i = 0; i < ETH_ALEN/2; i++)       //初始化MAC地址

{

dev->dev_addr[i*2] = eeprom_buff[i];

dev->dev_addr[i*2+1] = eeprom_buff[i] >> 8;

}

...

dev->netdev_ops    = &net_ops;        //初始化netdev_ops

...

retval = register_netdev(dev);        //注册网卡驱动

}

由代码可以看出

1、定义并分配net_device结构,使用alloc_etherdev函数。

2、初始化net_device。(包括中断号、I/O基地址、MAC地址、netdev_ops)

3、初始化硬件

4、将网卡驱动注册到内核,使用函数register_netdev

㈡发送数据

初始化netdev_ops时将其赋值为&net_ops,可在这个结构中找到发送函数

static const struct net_device_ops net_ops ={

.ndo_open=net_open,

.ndo_stop=net_close,

.ndo_tx_timeout=net_timeout,

.ndo_start_xmit=net_send_packet,

.ndo_get_stats=net_get_stats,

.ndo_set_multicast_list=set_multicast_list,

.ndo_set_mac_address=set_mac_address,

#ifdef CONFIG_NET_POLL_CONTROLLER

.ndo_poll_controller=net_poll_controller,#endif.ndo_change_mtu=eth_change_mtu,

.ndo_validate_addr=eth_validate_addr,

};

net_send_packet代码如下:

static netdev_tx_t net_send_packet(struct sk_buff *skb,struct net_device *dev)

{struct net_local *lp =netdev_priv(dev);

unsignedlongflags;if (net_debug > 3) {

printk("%s: sent %d byte packet of type %x\n",

dev->name, skb->len,

(skb->data[ETH_ALEN+ETH_ALEN] << 8) | skb->data[ETH_ALEN+ETH_ALEN+1]);

}spin_lock_irqsave(&lp->lock, flags);

netif_stop_queue(dev);/*initiate a transmit sequence*/writeword(dev->base_addr, TX_CMD_PORT, lp->send_cmd);

writeword(dev->base_addr, TX_LEN_PORT, skb->len);/*Test to see if the chip has allocated memory for the packet*/

if ((readreg(dev, PP_BusST) & READY_FOR_TX_NOW) == 0) {spin_unlock_irqrestore(&lp->lock, flags);if (net_debug) printk("cs89x0: Tx buffer not free!\n");returnNETDEV_TX_BUSY;

}/*Write the contents of the packet*/writewords(dev->base_addr, TX_FRAME_PORT,skb->data,(skb->len+1) >>1);

spin_unlock_irqrestore(&lp->lock, flags);

dev->stats.tx_bytes += skb->len;

dev_kfree_skb (skb);returnNETDEV_TX_OK;

}

这部分代码做了这些事情(红色高亮部分)

1、通知上层协议停止向网卡发送数据

由于网卡现在要向外发送数据包,所以要停止接收数据包

2、将skb中的数据写入寄存器中并发送走

3、释放skb空间

但是到这里并不算完,如果这就完了上层协议还是无法向网卡发送数据,网卡不能正常工作,显然这是不正常的。那么在什么地方重新允许上层协议向网卡发送数据包呢?

其实,当网卡发送走一个数据包后,会进入网卡中断程序中,查找request_irq的知中断处理程序名称为net_interrupt

static irqreturn_t net_interrupt(int irq, void *dev_id)

{

struct net_device *dev = dev_id;

struct net_local *lp;

int ioaddr, status;

int handled = 0;

ioaddr = dev->base_addr;

lp = netdev_priv(dev);

while ((status = readword(dev->base_addr, ISQ_PORT)))

{

switch(status & ISQ_EVENT_MASK)

{

...

case ISQ_TRANSMITTER_EVENT:

dev->stats.tx_packets++;

netif_wake_queue(dev);   /* Inform upper layers. */

if ((status & (    TX_OK |

TX_LOST_CRS |

TX_SQE_ERROR |

TX_LATE_COL |

TX_16_COL)) != TX_OK) {

if ((status & TX_OK) == 0)

dev->stats.tx_errors++;

if (status & TX_LOST_CRS)

dev->stats.tx_carrier_errors++;

if (status & TX_SQE_ERROR)

dev->stats.tx_heartbeat_errors++;

if (status & TX_LATE_COL)

dev->stats.tx_window_errors++;

if (status & TX_16_COL)

dev->stats.tx_aborted_errors++;

}

break;

...

}

}

}

4、通知上层协议,可以向网卡发送数据包。使用函数netif_wake_queue

㈢数据接收

当网卡接受到一个数据包后,进入网卡中断处理程序

static irqreturn_t net_interrupt(int irq, void *dev_id)

{struct net_device *dev =dev_id;struct net_local *lp;intioaddr, status;int handled = 0;

ioaddr= dev->base_addr;

lp=netdev_priv(dev);while ((status = readword(dev->base_addr, ISQ_PORT)))

{switch(status &ISQ_EVENT_MASK)

{

...

case ISQ_RECEIVER_EVENT:

/* Got a packet(s). */

net_rx(dev);

break;

}

}

}

net_rx函数代码如下

static voidnet_rx(struct net_device *dev)

{struct sk_buff *skb;intstatus, length;int ioaddr = dev->base_addr;

status= readword(ioaddr, RX_FRAME_PORT);

length = readword(ioaddr, RX_FRAME_PORT);if ((status & RX_OK) == 0) {

count_rx_errors(status, dev);return;

}/*Malloc up new buffer.*/skb = dev_alloc_skb(length + 2);if (skb ==NULL) {#if 0 /* Again, this seems a cruel thing to do */printk(KERN_WARNING"%s: Memory squeeze, dropping packet.\n", dev->name);#endifdev->stats.rx_dropped++;return;

}

skb_reserve(skb,2); /*longword align L3 header*/readwords(ioaddr, RX_FRAME_PORT, skb_put(skb, length), length>> 1);if (length & 1)

skb->data[length-1] = readword(ioaddr, RX_FRAME_PORT);if (net_debug > 3) {

printk("%s: received %d byte packet of type %x\n",

dev->name, length,

(skb->data[ETH_ALEN+ETH_ALEN] << 8) | skb->data[ETH_ALEN+ETH_ALEN+1]);

}

skb->protocol=eth_type_trans(skb,dev);

netif_rx(skb);

dev->stats.rx_packets++;

dev->stats.rx_bytes +=length;

}

由代码可以看出:

1、读取接收状态

2、读取接收到数据的长度

3、分配skb结构,skb = dev_alloc_skb(length + 2);

4、从硬件寄存器中读取数据存入skb

5、江封装好的数据包向上发送给协议栈,使用函数netif_rx

网卡驱动架构到这里大致就分析完了。如果有疑问或错误,欢迎指出。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值