dm9161和at91rm9200的收、发过程

在天涯上看到一系列分析比较透彻的文章,放到此处学习。

原文:http://blog.tianya.cn/blogger/post_show.asp?idWriter=0&Key=0&BlogID=803354&PostID=21127514

/*
 * 驱动内接收数据包过程
 * at91rm9200 - dm9161 - 2.6.20
 */

当MAC有数据包接收到,并且顺利的通过DMA拷贝到了驱动里设置的接收缓冲区之后,
会触发一个RCOM(接收完成)的中断,驱动在中断处理例程中调用at91ether_rx(dev)
进行处理。

首先,这里说明一下AT91rm9200的MAC硬件部分是怎么做接收缓冲的。


#define MAX_RBUFF_SZ 0x600 /* 1518 rounded up */
#define MAX_RX_DESCR 9 /* max number of receive buffers */

/* zhs: used in buflist entry's word0 */
#define EMAC_DESC_DONE 0x00000001 /* bit for if DMA is done */
#define EMAC_DESC_WRAP 0x00000002 /* bit for wrap */

#define EMAC_BROADCAST 0x80000000 /* broadcast address */
#define EMAC_MULTICAST 0x40000000 /* multicast address */
#define EMAC_UNICAST 0x20000000 /* unicast address */

struct rbf_t
{
 unsigned int addr; //descriptor's word0
 unsigned long size; //descriptor's word1
};

struct recv_desc_bufs
{
 struct rbf_t descriptors[MAX_RX_DESCR]; /* must be on sizeof (rbf_t) boundary */
 char recv_buf[MAX_RX_DESCR][MAX_RBUFF_SZ]; /* must be on long boundary */
};

struct at91_private
{
 struct net_device_stats stats;
 struct mii_if_info mii; /* ethtool support */
 struct at91_eth_data board_data; /* board-specific configuration */
 struct clk *ether_clk; /* clock */

 /* PHY */
 unsigned long phy_type; /* type of PHY (PHY_ID) */
 spinlock_t lock; /* lock for MDI interface */
 short phy_media; /* media interface type */
 unsigned short phy_address; /* 5-bit MDI address of PHY (0..31) */
 struct timer_list check_timer; /* Poll link status zhs: 用于轮询(没有中断) */

 /* Transmit */
 struct sk_buff *skb; /* holds skb until xmit interrupt completes. zhs: just for tx */
 dma_addr_t skb_physaddr; /* phys addr from pci_map_single */
 int skb_length; /* saved skb length for pci_unmap_single */

 /* Receive */
 int rxBuffIndex; /* index into receive descriptor list */
 struct recv_desc_bufs *dlist; /* descriptor list address. */
 struct recv_desc_bufs *dlist_phys; /* descriptor list physical address */
}


以上的宏和结构体定义提供了对于at91rm9200的MAC硬件接收和发送的支持。这里说
明下接收的情况。

如果一个包接收后,经过地址验证后没问题的话,该包被DMA存储到接收缓冲中去。

接收缓冲区是为MAX_RBUFF_SZ大小的一个内存空间,这里定义了MAX_RX_DESCR个接收
缓冲区。其实以太网内最大的包大小为1522.

驱动需要提供给MAC硬件的缓冲区的格式是MAC硬件要求的规定格式.多个的接收缓冲
区被组织成一个list。这个list被叫作descriptor list(描述符列表)。每个list的
节点为一个叫作descriptor(dp)或者一个list entry,每个dp有两个word组成,分别
为word0和word1,结构体 struct rbf_t就为这个list entry,结构体内的addr就为
word0, size为word1。因为 int 和 long 类型都是4字节的,所以这个结构体是字对
齐的,不用担心hole问题。

而list这个结构则有结构体 struct recv-desc_bufs来维护,里面有两个成员变量.
一个是dp的数组,为实际的list,即这个数组就是list的实体。 另一个为缓冲区的
数组,每个缓冲区大小为MAX_RBUFF_SZ,有MAX_RX_DESCR个缓冲区。

在 struct at91_private结构体中的 Receive段有三个成员变量来维护接收缓冲区.
int rxBuffIndex表示当前软件中处理的list的入口标号,即是对那个缓冲区进行处
理。struct recv_desc_bufs *dlist指向descriptor list的虚拟内存地址,在驱动
中调用并操作list。struct recv_desc_bufs *dlist_phys用来保存描述符列表的物
理地址,便于MAC硬件和DMA处理。而对于dlist_phys的初始化在at91ether_setup中
进行。

至于MAC对接收缓冲列表的具体处理过程和list entry每个word代表的含义可以参考
at91rm9200的datasheet。

void at91ether_rx(struct net_device *dev) 这个函数在MAC的中断处理例程中被
调用,在中断上下文中对接收到的包进行处理,并提交给内核上层。

at91ether_rx(dev)
 |
 取得private和dlist数据
 |
 循环检查list的每个entry
 |
 -----------------------------
 | |
 如果缓冲区中有新包数据 如果没有新包数据
 | |
 取出实际数据包头地址和长度 |
 这个时候的包地址已经是虚拟 |
 内存地址了 |
 | |
 分配skb套接字缓冲区 |
 | |
 拷贝包数据到skb中 |
 | |
 设置skb其他成员变量 |
 | |
 netif_rx(skb) |
 传输给内核上层 |
 | |
 标记该缓冲区已处理 |
 | |
 对index处理 |
 | |
 -------------------------------------
 |
|


/*
 * 驱动内发送数据包过程
 * at91rm9200 - dm9161 - 2.6.20
 */

dev_queue_xmit --> dev_hard_start_xmit --> hard_start_xmit(指向驱动函数)

以上为发送数据包的大致流程。来自IP层的的一个套接字结构体struct sk_buff存
放在内核维护的发送队列中。在发送队列能够发送一个套接字缓冲区时,调用这个
函数:dev_queue_xmit(). 该函数的参数为 struct sk_buff *skb, 通过skb->dev
可以取得这个套接字缓冲区所要发送到的设备, struct net_device dev = skb->dev;
获得dev之后,就可以获得操作该网络设备的能力了。检测网络设备硬件状况,如果
符合要求能够发送数据包,则调用dev_hard_start_xmit(skb, dev)发送包。

在int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)中
通过dev->hard_start_xmit(skb, dev)来执行实际的发送动作.这里dev的成员函数
hard_start_xmit()是一个函数指针,在dev的驱动初始化过程中应该应该初始化好
了的。对于at91rm9200-dm9161,该函数为at91_ether.c中的at91ether_tx()函数.

int at91ether_tx(struct sk_buff *skb, struct net_device *dev)函数中,执行
的流程如下:

at91ether_tx(skb, dev);
 |
 取得private数据
 |
 检查EMAC寄存器,硬件是否可以发送数据包
 |
 ------------------------------------------
 | |
 如果可以发送 如果不能发送
 | |
 netif_stop_queue(dev) 打印一条设备忙信息
 暂停发送队列,即不启动其他包发送 |
 可以在硬件发送完成的中断中唤醒队列 返回1
 | |
 将skb相关信息配置到private的发送相 |
 关的成员变量中,并配置发送的DMA. |
 | |
 配置好之后,写EMAC,包括数据包的物 |
 理地址和发送长度,即开始一次硬件发 |
 送 |
 | |
 返回0 |
 | |
|<------------------------------------------------

这里对于虚拟内存中的套接字缓冲区skb,调用了以下的函数:
 lp->skb_physaddr = dma_map_single(NULL, skb->data, skb->len, DMA_TO_DEVICE);
这个函数应该是将虚拟内存的地址转化为物理地址,并存放在private中的成员变量
skb_physaddr中,再调用at91_emac_write(AT91_EMAC_TAR, lp->skb_physaddr);将
要发送的套接字缓冲区物理地址写到EMAC的Transmit Address Register中,这样就
可以在发送这个物理硬件过程和DMA中直接处理物理地址。

这个发送函数的返回值不同,则处理有不同。如果返回的是1,即不能发送,则调用
驱动函数的dev_queue_xmit函数会在调用这个驱动函数之后释放掉skb套接字缓冲区
占用的内存空间;而如果返回的是0,即交由了硬件发送,则在发送完成(包括发送
失败的情况)的中断处理例程中,会释放掉由private中成员变量所指向的套接字缓
冲

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值