http://bbs.chinaunix.net/thread-1960516-1-1.html
9344 ethernet数据接收代码分析
在板的ethernet只有一个网卡,在系统处理话的时候有如下类似代码:
for(i = 0; i < ATHR_GMAC_NMACS; i++)
{
dev = alloc_etherdev(ATHR_MAC_ETHERDEV_SZ);
if (!dev)
{
printk(MODULE_NAME ": unable to allocate mac\n");
return 1;
}
ATHR_MAC_NETPRIV(mac,dev);
memset(mac, 0, sizeof(athr_gmac_t));
mac->mac_dev = dev;
mac->mac_unit = i;
mac->mac_base = athr_gmac_base(i);
mac->mac_irq = athr_gmac_irq(i);//以太网申请的中断号。
mac->mac_noacs = 1;
mac->num_tx_desc = athr_tx_desc_num[i];
mac->num_rx_desc = athr_rx_desc_num[i];
mac->reap_thresh = athr_tx_desc_num[i] / 2;
mac->qstart_thresh = 4 * tx_max_desc_per_ds_pkt;
mac->mac_speed = ATHR_PHY_SPEED_UNKNOWN;
..............................................
}
在之前的中断处理过程说明过,我们在申请中断之前,必须将中断注册到OS中。
/proc # cat interrupts
CPU0
4: 3604 MIPS eth0
6: 0 MIPS cascade
7: 737861 MIPS timer
18: 0 ATH MISC cascade
19: 317 ATH MISC serial
48: 0 ATH GPIO SW JUMPSTART/FACTORY RESET
有上面的可以知道,我们申请的中断号为4,其代码中也有定义#define ATH_CPU_IRQ_GE0 ATH_CPU_IRQ_BASE+4
当收到数据时,会触发中断:其执行如下代码:
asmlinkage void plat_irq_dispatch(void)
{
int pending = read_c0_status() & read_c0_cause();
#if 0
if (!(pending & CAUSEF_IP7))
printk("%s: in irq dispatch \n", __func__);
#endif
if (pending & CAUSEF_IP7) {
do_IRQ(ATH_CPU_IRQ_TIMER);
ath_aphang_timer_fn();
}
else if (pending & CAUSEF_IP2)
ath_dispatch_wlan_intr();
else if (pending & CAUSEF_IP4) //操作系统调度执行注册的中断处理函数。
do_IRQ(ATH_CPU_IRQ_GE0);
else if (pending & CAUSEF_IP5)
do_IRQ(ATH_CPU_IRQ_GE1);
else if (pending & CAUSEF_IP3)
do_IRQ(ATH_CPU_IRQ_USB);
else if (pending & CAUSEF_IP6)
ath_dispatch_misc_intr();
/*
* Some PCI devices are write to clear. These writes are posted and might
* require a flush (r8169.c e.g.). Its unclear what will have more
* performance impact - flush after every interrupt or taking a few
* "spurious" interrupts. For now, its the latter.
*/
/*else
printk("spurious IRQ pending: 0x%x\n", pending); */
}
注册的中断处理函数如下:
st = request_irq(mac->mac_irq, athr_gmac_intr, ATHR_MAC_IRQF_DISABLED, dev->name, dev);
if (st < 0)
{
printk(MODULE_NAME ": request irq %d failed %d\n", mac->mac_irq, st);
return 1;
}
static irqreturn_t athr_gmac_intr(ATHR_MAC_ISR_ARGS)
{
struct net_device *dev = (struct net_device *)dev_id;
athr_gmac_t *mac = (athr_gmac_t *)ATHR_MAC_PRIV(dev);
int isr, imr, handled = 0;
isr = athr_gmac_get_isr(mac); //取得中断状态寄存器的值
imr = athr_gmac_reg_rd(mac, ATHR_GMAC_DMA_INTR_MASK);
athr_gmac_trc(isr,"isr");
athr_gmac_trc(imr,"imr");
assert(mac->mac_ifup);
assert(isr == (isr & imr));
if (isr & (ATHR_GMAC_INTR_RX_OVF))
{
handled = 1;
if (is_ar7240() || is_ar933x())
{
athr_gmac_reg_wr(mac,ATHR_GMAC_CFG1,(athr_gmac_reg_rd(mac,ATHR_GMAC_CFG1)&0xfffffff3));
}
athr_gmac_intr_ack_rxovf(mac);
}
if (likely(isr & ATHR_GMAC_INTR_RX)) //接收中断触发,是分析的重点。
{
handled = 1;
if (mac_has_flag(mac,ATHR_RX_POLL))
{
if (likely(athr_mac_rx_sched_prep(mac,dev)))
{
int status = athr_gmac_reg_rd(mac, ATHR_GMAC_DMA_RX_STATUS);
athr_gmac_intr_disable_recv(mac);
assert((status & ATHR_GMAC_RX_STATUS_PKT_RCVD));
assert((status >> 16));
__athr_mac_rx_sched(mac,dev);
}
else
{
//printk(MODULE_NAME ": driver bug! interrupt while in poll\n");
//assert(0);
athr_gmac_intr_disable_recv(mac);
}
}
else
{
athr_gmac_intr_disable_recv(mac);
ATH_SCHEDULE_TQUEUE(&mac->rxtq,mac);
}
}
if (likely(isr & ATHR_GMAC_INTR_TX))
{
handled = 1;
athr_gmac_intr_ack_tx(mac);
athr_gmac_intr_disable_tx(mac);
ATH_SCHEDULE_TQUEUE(&mac->txreaptq,mac);
}
if (unlikely(isr & ATHR_GMAC_INTR_RX_BUS_ERROR))
{
assert(0);
handled = 1;
athr_gmac_intr_ack_rxbe(mac);
}
if (unlikely(isr & ATHR_GMAC_INTR_TX_BUS_ERROR))
{
assert(0);
handled = 1;
athr_gmac_intr_ack_txbe(mac);
}
if (!handled)
{
/*
* Mac fast reset will clear the status register
* if we get a previously queued interrupt after reset
* ignore it.
*/
if (mac->dma_check)
{
mac->dma_check = 0;
return IRQ_HANDLED;
}
printk(MODULE_NAME ": unhandled intr isr %#x\n", isr);
assert(0);
}
return IRQ_HANDLED;
}
由于在SDK中,我们定义了,#define CONFIG_ATHR_RX_TASK 1故在中断处理函数中时候,中断上下半部分。
使用的是tasklet
if (likely(isr & ATHR_GMAC_INTR_RX)) //没有使用查询模式,此模式时将会涉及到NAPI机制来接收数据包。
{
handled = 1;
if (mac_has_flag(mac,ATHR_RX_POLL))
{
if (likely(athr_mac_rx_sched_prep(mac,dev)))
{
int status = athr_gmac_reg_rd(mac, ATHR_GMAC_DMA_RX_STATUS);
athr_gmac_intr_disable_recv(mac);
assert((status & ATHR_GMAC_RX_STATUS_PKT_RCVD));
assert((status >> 16));
__athr_mac_rx_sched(mac,dev);
}
else
{
//printk(MODULE_NAME ": driver bug! interrupt while in poll\n");
//assert(0);
athr_gmac_intr_disable_recv(mac);
}
}
else
{
athr_gmac_intr_disable_recv(mac);//禁止中断
ATH_SCHEDULE_TQUEUE(&mac->rxtq,mac); //调度tasklet。
}
}
中断处理的下半部定义如下:
#define ATH_INIT_RX_TASK() ATH_INIT_TQUEUE(&mac->rxtq,athr_gmac_recv_packets,mac);
athr_gmac_recv_packets()函数定义如下:
athr_receive_pkt() //这个函数兼顾中断俩种处理方式NAPI和完全中断的方式。
{
ATHR_TASK_DEFS()
athr_gmac_ring_t *r = &mac->mac_rxring;
athr_gmac_desc_t *ds;
athr_gmac_buffer_t *bp;
struct sk_buff *skb;
athr_gmac_rx_status_t ret = ATHR_GMAC_RX_STATUS_DONE;
int head = r->ring_head, len, status, iquota = quota, more_pkts, rep;
#ifdef MEMLAT_OPT
athr_gmac_desc_t ds_pre[2];
int head_pre;
#endif
athr_gmac_trc(iquota,"iquota");
status = athr_gmac_reg_rd(mac, ATHR_GMAC_DMA_RX_STATUS);
process_pkts:
athr_gmac_trc(status,"status");
assert(mac->mac_ifup);
/*
* Flush the DDR FIFOs for our gmac
*/
athrs_flush_ge(mac->mac_unit);
assert(quota > 0); /* WCL */
if (mac->dma_check) {
mac->dma_check = 0;
}
else {
ATHR_NAPI_CHECK_STATUS();
}
#ifdef MEMLAT_OPT
head_pre = head;
ds_pre[quota & 1] = r->ring_desc[head_pre];
#endif
while(quota)
{
#ifndef MEMLAT_OPT
ds = &r->ring_desc[head];
#else
ds = ds_pre + (quota & 1);
if (quota - 1) {
athr_gmac_ring_incr(head_pre);
ds_pre[(quota & 1) ^ 1] = r->ring_desc[head_pre];
}
#endif
athr_gmac_trc(head,"hd");
athr_gmac_trc(ds, "ds");
if (athr_gmac_rx_owned_by_dma(ds))
{
assert(quota != iquota); /* WCL */
break;
}
athr_gmac_intr_ack_rx(mac);
bp = &r->ring_buffer[head];
len = ds->pkt_size;
skb = bp->buf_pkt;
assert(skb);
skb_put(skb, len - ETHERNET_FCS_SIZE);
/*
*Data corruption sometimes with large buffer size of 4k.Cached region
*is not synced with non cached region putting temporary fix for
*invalidating cache contents Need to find the root cause.
*/
athr_mac_cache_inv((unsigned long)(skb->data), skb->len);
athr_ssdk_process_arp_header(mac, skb);
athr_gmac_rx_qos(mac,skb);
athr_gmac_vlan_igmp(mac,skb);
mac->net_rx_packets ++;
mac->net_rx_bytes += skb->len;
/*
* also pulls the ether header
*/
skb->protocol = eth_type_trans(skb, dev);
skb->dev = dev;
bp->buf_pkt = NULL;
dev->last_rx = jiffies;
quota--;
athr_nat_process_ingress_pkts(mac->mac_unit, skb, ds);
netif_receive_skb(skb); //将接受的数据送往协议栈
if (mac->rx_dma_check) {
mac->rx_dma_check = 0;
}
athr_gmac_ring_incr(head);
}
assert(iquota != quota);
r->ring_head = head;
rep = athr_gmac_rx_replenish(mac);
/*
* let's see what changed while we were slogging.
* ack Rx in the loop above is no flush version. It will get flushed now.
*/
status = athr_gmac_reg_rd(mac, ATHR_GMAC_DMA_RX_STATUS);
more_pkts = (status & ATHR_GMAC_RX_STATUS_PKT_RCVD);
athr_gmac_trc(more_pkts,"more_pkts");
if (!more_pkts) goto done;
/*
* more pkts arrived; if we have quota left, get rolling again
*/
if (quota) goto process_pkts;
/*
* out of quota
*/
ret = ATHR_GMAC_RX_STATUS_NOT_DONE;
done:
if (mac_has_flag(mac,ATHR_RX_POLL))
*work_done = (iquota - quota);
if (mac_has_flag(mac,ATHR_RX_POLL) &&
unlikely(athr_gmac_rx_ring_full(mac)))
return GMAC_RX_STATUS_OOM;
/*
* !oom; if stopped, restart h/w
*/
if (unlikely(status & ATHR_GMAC_RX_STATUS_OVF))
{
mac->net_rx_over_errors ++;
athr_gmac_intr_ack_rxovf(mac);
athr_gmac_rx_start(mac);
}
if (mac_has_flag(mac,ATHR_RX_TASK))
athr_gmac_intr_enable_recv(mac);
RX_RETURN();
}
上面的接收处理函数中,重要的结构参数ring_buffer[]不好理解,还需要更升入的研究。
需要注意的是:每中断一次,都会禁止中断,之后在tasklet中来接收数据,数据接收完成之后有开启中断。
我们对capwap协议的处理放在了驱动中进行处理,故接收和发送函数都需要对capwap协议进行处理。
注意:不能在中断和查询中占用太多的时间。
9344中数据接收兼容了中断和查询这俩种方式来处理数据接收。在性能方面不知道那个要好一些。