您的位置:首页 > 运维架构 > Linux

Linux-2.6.20的cs8900驱动分析(三)

2015-07-15 16:29 561 查看
本博客转载于:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=22535114&id=1773576

三、net_rx和net_send_packet

3.1 net_rx

在这部分将介绍cs8900驱动的两个最重要的函数,内核通过该两个函数实现了数据的收发。net_rx函数的主要功能是从cs8900的片上数据缓冲区中将数据传送给sk_buff缓冲区,sk_buff是网络驱动程序与Linux内核通信的缓冲区。该结构可在\include\linux\skbuff.h中找到。net_rx函数的功能可总结如下:(该总结来源于:http://www.akae.cn/bbs/archiver/?tid-6657.html)

A.获取私有数据存放于lp中;

B.获取设备缓冲区状态和缓冲长度;

C.如果状态不为RX_OK则计数接收数据错误次数count_rx_error()

D.分配一个sk_buf区间

E.字对齐,skb_reserve();

F.插入数据到接收口,insw();

G.写入数据;

H.初始化sk_buff结构,eth_type_trans()

I.进入上层接收函数netif_rx();

J.初始化设备的计数;



net_rx函数的注解如下所示:

static void net_rx(struct net_device *dev)

{

struct net_local *lp = netdev_priv(dev); //lp指向驱动程序的私有数据区

struct sk_buff *skb; //申请skb_buff指针

int status, length;



int ioaddr = dev->base_addr; // 得到cs8900的基地址

status = readword(ioaddr, RX_FRAME_PORT); //获取cs8900片上缓冲区的状态

length = readword(ioaddr, RX_FRAME_PORT); //获取cs8900片上缓冲区的长度



if ((status & RX_OK) == 0) { //状态为接收错误,调用count_rx_errors统计错误

count_rx_errors(status, lp);

return;

}



/* Malloc up new buffer. */

skb = dev_alloc_skb(length + 2); //分配一个缓冲区,dev_alloc_skb函数以

//GFP_ATOMIC优先级调用alloc_skb。alloc_skb的功能为分配一个缓冲区

//并初始化skb->data,skb->tail和skb_head域。dev_alloc_skb和alloc_skb的区

//别为,前者在skb->data和skb_head之间保留了一些空间,网络层使用这

//一数据空间进行优化工作,驱动程序不该访问该空间。

if (skb == NULL) { //skb缓冲区分配失败?

……

lp->stats.rx_dropped++; //直接将丢包数加1

return;

}

skb_reserve(skb, 2); /* longword align L3 header */ //该函数增加skb的data和tail,

//该函数可填充缓冲区之前保留报文头空间,大多数以太网在数据包之前

//保留2个字节,这样IP头可在14字节的以太网头之后,在16字节边界上对

//齐。这里也空了两个字节,这两个自己加上14字节的以太网头刚好16字

//节。所以这里的主要作用是字对齐。

skb->dev = dev;



readwords(ioaddr, RX_FRAME_PORT, skb_put(skb, length), length >> 1); //skb_put函

//数的作用是更新skb的tail和len成员,也即在缓冲区尾部添加数据,该函数返

//回skb->tail的先前值。整句代码的含义为,从cs8900的数据缓冲区中读取

//length个字节数据到skb缓冲区。由于readwords是以读取字(两个字节)为

//单位,所以length应该保持字对齐,也即length右移一位。

if (length & 1) //因为前面length以字对齐,如果length为单字节,

//所以这里应该补上最后一个字节

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

……

skb->protocol=eth_type_trans(skb,dev); //该函数定义在linux/net/ethernet/eth.c中,

//该处可参见linux设备驱动程序相关章节

netif_rx(skb); //通知内核已经接收到一个数据包,并封装入一个套接字缓冲区

dev->last_rx = jiffies; //更新最后的接收包时间

lp->stats.rx_packets++; //接收的总数据包数加1

lp->stats.rx_bytes += length; //接收的字节数加上length

}



3.1 net_send_packet

net_send_parcket为内核提供了数据包发送功能,该函数在cs89x0_probe1中被赋予了net_device的hard_start_xmit域,当内核需要发送数据包时,将调用dev->
hard_start_xmit完成最后的数据包发送。该函数被调用的前提是,在调用该函数之前,内核已经将数据包放入了skb缓冲区中。该函数的主要任务有:

A.获取设备私有数据指针

B.加环形锁,spin_lock_irq();

C.检测缓冲区是否为满,若满则调用netif_stop_queue()暂停发送队列;

D.写发送命令和发送长度,writeword();

E.读取发送总线状态readreg();

F.解环形锁,spin_unlock_irq();

G.设置传输时钟计数;

H.释放相应sk_buff, dev_kfree_skb().

下面为此函数的简单注释:

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

{

struct net_local *lp = netdev_priv(dev); //获得驱动程序的私有数据



……



spin_lock_irq(&lp->lock); //获得自旋锁,以便进入临界区

netif_stop_queue(dev); //通知内核暂停内核与驱动程序间的数据传递,也即告诉

//内核不要向skb缓冲区填充数据。



/* initiate a transmit sequence */ //初始化cs8900的发送对列,主要为写命令和数

//据长度,为数据发送做准备

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 * / //查看cs8900是否为

//发送分配了地址空间。

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 */

writewords(dev->base_addr, TX_FRAME_PORT,skb->data,(skb->len+1) >>1); //将数

//据交给cs8900发送

spin_unlock_irq(&lp->lock); //发送结束,释放自旋锁

lp->stats.tx_bytes += skb->len; //累加发送的总字节数

dev->trans_start = jiffies; //更新最后的传输时间

dev_kfree_skb (skb); //发送完毕,释放skb缓冲区

……

return 0;

}

总结:

在cs8900驱动中,主要简解了驱动程序中的部分重要函数,包括初始化、打开/关闭网络驱动和发送/接收数据。对于其余的驱动程序代码,如超时处理、状态获取等函数没做解释,它们的实现也比较简单。由于自己板子上没有EEPROM,所以也没有分析与EEPROM相关部分的代码。DMA部分好像编译进去会错,所以也没有去分析,以后有时间再去弄弄DMA部分。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: