linux网卡驱动和tcp,TCP/IP协议在Linux2.4上的实现 之 网卡驱动篇

先来介绍下,物理层网卡的硬件驱动,实验板采用的是Xscale的CPU,网卡芯片是CS8900,运行的操作系统为Linux2.4的核,很多代码在2.6中已经有很大改变了。

下面所显示的代码目录在/linux/drivers/net/cs89x0.c下。

先来介绍下,物理层网卡的硬件驱动,实验板采用的是Xscale的CPU,网卡芯片是CS8900,运行的操作系统为Linux2.4的核,很多代码在2.6中已经有很大改变了。

下面所显示的代码目录在/linux/drivers/net/cs89x0.c下。

net_device 结构体定义在linux/include/linux/netdevice.h中

net_local 结构体定义在/linux/drivers/net/cs89x0.c中

int init_module(void) //初始化模块函数

{

struct net_local *lp;

int ret = 0;

//dev_cs89x0 为 net_device 结构

dev_cs89x0.irq = irq;//分配中断号

dev_cs89x0.base_addr = io;//指定IO地址

dev_cs89x0.init = cs89x0_probe;//检测实际的网络设备是否存在,在space.c中完成了cs89x0_probe在isa_probes管理结构体管理上的注册

dev_cs89x0.priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);//指向net_local结构的一个指针

if (dev_cs89x0.priv == 0) {

printk(KERN_ERR "cs89x0.c: Out of memory./n");

return -ENOMEM;

}

memset(dev_cs89x0.priv, 0, sizeof(struct net_local));

lp = (struct net_local *)dev_cs89x0.priv;

spin_lock_init(&lp->lock);

。。。。。。 省略部分代码

if (io == 0) {

printk(KERN_ERR "cs89x0.c: Module autoprobing not allowed./n");

printk(KERN_ERR "cs89x0.c: Append io=0xNNN/n");

ret = -EPERM;

goto out;

}

// 注册net_device结构体

if (register_netdev(&dev_cs89x0) != 0) {

printk(KERN_ERR "cs89x0.c: No card found at 0x%x/n", io);

ret = -ENXIO;

goto out;

}

out:

if (ret)

kfree(dev_cs89x0.priv);

return ret;

}

void cleanup_module(void)

{

if (dev_cs89x0.priv != NULL) {

/* Free up the private structure, or leak memory :-) */

unregister_netdev(&dev_cs89x0);

outw(PP_ChipID, dev_cs89x0.base_addr + ADD_PORT);

kfree(dev_cs89x0.priv);

dev_cs89x0.priv = NULL;/* gets re-allocated by cs89x0_probe1 */

/* If we don't do this, we can't re-insmod it later. */

release_region(dev_cs89x0.base_addr, NETCARD_IO_EXTENT);//释放虚拟地址对应的物理地址

}

}

static int net_open(struct net_device *dev)

{ //对应命令 ifconfig eth up

申请中断,reques_irq();

初始化网络的内部数据结构;

运行硬件的初始化;

netif_start_queue(dev);//设置发送队列标记

}

static int net_close(struct net_device *dev)

{ //对应命令 ifconfig eth down

struct net_local *lp = (struct net_local *)dev->priv;

//停止发送队列标记

netif_stop_queue(dev);

//使硬件停止工作

writereg(dev, PP_RxCFG, 0);

writereg(dev, PP_TxCFG, 0);

writereg(dev, PP_BufCFG, 0);

writereg(dev, PP_BusCTL, 0);

//释放中断

free_irq(dev->irq, dev);

/* Update the statistics here. */

return 0;

}

网络设备的发送:从内核堆栈上获得数据,再传输到网络硬件上,

网络设备的接收:从网络硬件上获得数据,再传输到内核堆栈上

内核堆栈的数据类型为:sk_buffer结构

发送函数:int(*hard_start_xmit) (struct sk_buff *skb, struct net_device *dev);

static int (*hard_start_xmit) (struct sk_buff *skb, struct net_device *dev)

{

struct net_local *lp = (struct net_local *)dev->priv;

spin_lock_irq(&lp->lock);

netif_stop_queue(dev);// 停止更新发送队列

/* initiate a transmit sequence */

writeword(dev, TX_CMD_PORT, lp->send_cmd);//发送命令

writeword(dev, 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_irq(&lp->lock);

if (net_debug) printk("cs89x0: Tx buffer not free!/n");

return 1;

}

/* Write the contents of the packet */

outsw(dev->base_addr + TX_FRAME_PORT,skb->data,(skb->len+1) >>1);//往网卡里边写数据

spin_unlock_irq(&lp->lock);

dev->trans_start = jiffies;//纪录发送的开始时间

dev_kfree_skb (skb);

return 0;

}

中断函数:网卡采用中断共享的方法,request_irq(SA_SHIRQ)。在中断服务函数net_interrupt中通过读取网卡硬件端口 readword(dev, ISQ_PORT)来判断发生了何种事件。其中分别对应于

ISQ_RECEIVER_EVENT :当硬件接收到一个数据帧时,此时将dev对应的数据拷贝,再交给上层处理

,这里调用了net_rx函数,完成后续工作。

ISQ_TRANSMITTER_EVENT:当发生发送事件,ISQ_BUFFER_EVENT:网卡有足够发送空间

二者都是调用了函数netif_wake_queue(dev);此函数的功能,一个是用来删除dev中已发送的队列

二来是从dev的队列中取出skb结构来再次发送数据。

ISQ_RX_MISS_EVENT、ISQ_TX_COL_EVENT,是对Miss,Collsion两个错误情况的处理,无非是一些统计。

static void net_interrupt(int irq, void *dev_id, struct pt_regs * regs)

{

while ((status = readword(dev, ISQ_PORT))) {

if (net_debug > 4)printk("%s: event=%04x/n", dev->name, status);

switch(status & ISQ_EVENT_MASK) {

case ISQ_RECEIVER_EVENT://硬件收到数据

net_rx(dev);//硬件收到数据,递交给内核堆栈

break;

case ISQ_TRANSMITTER_EVENT://数据包发送完毕

lp->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) lp->stats.tx_errors++;

if (status & TX_LOST_CRS) lp->stats.tx_carrier_errors++;

if (status & TX_SQE_ERROR) lp->stats.tx_heartbeat_errors++;

if (status & TX_LATE_COL) lp->stats.tx_window_errors++;

if (status & TX_16_COL) lp->stats.tx_aborted_errors++;

}

break;

case ISQ_BUFFER_EVENT://确定有空间发送,可以发送数据

if (status & READY_FOR_TX) {//发送执行成功

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

}

if (status & TX_UNDERRUN) {//发送没有执行成功

if (net_debug > 0) printk("%s: transmit underrun/n", dev->name);

lp->send_underrun++;

if (lp->send_underrun == 3) lp->send_cmd = TX_AFTER_381;

else if (lp->send_underrun == 6) lp->send_cmd = TX_AFTER_ALL;

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

}

break;

case ISQ_RX_MISS_EVENT://接收丢失

lp->stats.rx_missed_errors += (status >>6);

break;

case ISQ_TX_COL_EVENT://发送碰撞

lp->stats.collisions += (status >>6);

break;

}

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值