Chinaunix首页 | 论坛 | 博客
  • 博客访问: 770983
  • 博文数量: 370
  • 博客积分: 2334
  • 博客等级: 大尉
  • 技术积分: 3222
  • 用 户 组: 普通用户
  • 注册时间: 2012-05-06 16:56
文章分类

全部博文(370)

文章存档

2013年(2)

2012年(368)

分类:

2012-05-21 13:27:24

下面分析一个重要的函数--中断处理函数

static irqreturn_t dmfe_interrupt(int irq, void *dev_id)
{
        struct net_device *dev = dev_id;
        board_info_t *db;
        int int_status,i;
        u8 reg_save;
        /* A real interrupt coming */
        db = (board_info_t *)dev->priv;
        spin_lock(&db->lock);
        /* Save previous register address */
        reg_save = GETB(db->io_addr);
        /* Disable all interrupt */
        iow(db, DM9KS_IMR, DM9KS_DISINTR);
        /* Got DM9000A/DM9010 interrupt status */
        int_status = ior(db, DM9KS_ISR); /* Got ISR */
        iow(db, DM9KS_ISR, int_status); /* Clear ISR status */
        /* Link status change */
        if (int_status & DM9KS_LINK_INTR)
        {
                netif_stop_queue(dev);
                for(i=0; i<500; i++) /*wait link OK, waiting time =0.5s */
                {
                        phy_read(db,0x1);
                        if(phy_read(db,0x1) & 0x4) /*Link OK*/
                        {
                                /* wait for detected Speed */
                                for(i=0; i<200;i++)
                                        udelay(1000);
                                /* set media speed */
                                if(phy_read(db,0)&0x2000) db->Speed =100;
                                else db->Speed =10;
                                break;
                        }
                        udelay(1000);
                }
                netif_wake_queue(dev);
                //printk("[INTR]i=%d speed=%d\n",i, (int)(db->Speed));

        }
        /* Received the coming packet */
        if (int_status & DM9KS_RX_INTR)
                dmfe_packet_receive(dev);
        /* Trnasmit Interrupt check */
        if (int_status & DM9KS_TX_INTR)
                dmfe_tx_done(0);
        
        if (db->cont_rx_pkt_cnt>=CONT_RX_PKT_CNT)
        {
                iow(db, DM9KS_IMR, 0xa2);
        }
        else
        {
                /* Re-enable interrupt mask */
                iow(db, DM9KS_IMR, DM9KS_REGFF);
        }
        
        /* Restore previous register address */
        PUTB(reg_save, db->io_addr);
        spin_unlock(&db->lock);
        return IRQ_HANDLED;
}

前面已经提到,注册中断的时候该函数作为request_irq()函数的第二个参数。发生中断时,系统将调用该函数进行相关处理。

函数首先需要获得自旋锁,然后将当前的寄存器地址保存下来,以便返回的时候继续进行被打断的作业;接着就是屏蔽所有的中断,读取中断状态寄存器并清除中断状态寄存器,然后就开始真正的中断处理了。

在进行中断处理之前,需要首先判断是发生了什么中断。有如下几种可能:连接状态改变、数据接收中断或者数据发送中断。连接状态改变的处理比较简单,就不讨论了。

首先看数据接收中断。当发生接收中断时,中断函数调用dmfe_packet_receive()函数

static void dmfe_packet_receive(struct net_device *dev)
{
        board_info_t *db = (board_info_t *)dev->priv;
        struct sk_buff *skb;
        u8 rxbyte, val;
        u16 i, GoodPacket, tmplen = 0, MDRAH, MDRAL;
        u32 tmpdata;
        rx_t rx;
        u16 * ptr = (u16*)&rx;
        u8* rdptr;
        do {
                /*store the value of Memory Data Read address register*/
                MDRAH=ior(db, DM9KS_MDRAH);
                MDRAL=ior(db, DM9KS_MDRAL);
                
                ior(db, DM9KS_MRCMDX); /* Dummy read */
                rxbyte = GETB(db->io_data); /* Got most updated data */
                /* packet ready to receive check */
                if(!(val = check_rx_ready(rxbyte))) break;
                /* A packet ready now & Get status/length */
                GoodPacket = TRUE;
                PUTB(DM9KS_MRCMD, db->io_addr);
                /* Read packet status & length */
                switch (db->io_mode)
                        {
                          case DM9KS_BYTE_MODE:
                                     *ptr = GETB(db->io_data) +
                                               (GETB(db->io_data) << 8);
                                    *(ptr+1) = GETB(db->io_data) +
                                            (GETB(db->io_data) << 8);
                                    break;
                          case DM9KS_WORD_MODE:
                                    *ptr = GETW(db->io_data);
                                    *(ptr+1) = GETW(db->io_data);
                                    break;
                          case DM9KS_DWORD_MODE:
                                    tmpdata = GETL(db->io_data);
                                    *ptr = tmpdata;
                                    *(ptr+1) = tmpdata >> 16;
                                    break;
                          default:
                                    break;
                        }
                /* Packet status check */
                if (rx.desc.status & 0xbf)
                {
                        GoodPacket = FALSE;
                        if (rx.desc.status & 0x01)
                        {
                                db->stats.rx_fifo_errors++;
                                printk("\n");
                        }
                        if (rx.desc.status & 0x02)
                        {
                                db->stats.rx_crc_errors++;
                                printk("\n");
                        }
                        if (rx.desc.status & 0x80)
                        {
                                db->stats.rx_length_errors++;
                                printk("\n");
                        }
                        if (rx.desc.status & 0x08)
                                printk("\n");
                }
                if (!GoodPacket)
                {
                        // drop this packet!!!

                        switch (db->io_mode)
                        {
                                case DM9KS_BYTE_MODE:
                                         for (i=0; i<rx.desc.length; i++)
                                                GETB(db->io_data);
                                        break;
                                case DM9KS_WORD_MODE:
                                        tmplen = (rx.desc.length + 1) / 2;
                                        for (i = 0; i < tmplen; i++)
                                                GETW(db->io_data);
                                        break;
                                case DM9KS_DWORD_MODE:
                                        tmplen = (rx.desc.length + 3) / 4;
                                        for (i = 0; i < tmplen; i++)
                                                GETL(db->io_data);
                                        break;
                        }
                        continue;/*next the packet*/
                }
                
                skb = dev_alloc_skb(rx.desc.length+4);
                if (skb == NULL )
                {
                        printk(KERN_INFO "%s: Memory squeeze.\n", dev->name);
                        /*re-load the value into Memory data read address register*/
                        iow(db,DM9KS_MDRAH,MDRAH);
                        iow(db,DM9KS_MDRAL,MDRAL);
                        return;
                }
                else
                {
                        /* Move data from DM9000 */
                        skb->dev = dev;
                        skb_reserve(skb, 2);
                        rdptr = (u8*)skb_put(skb, rx.desc.length - 4);
                        
                        /* Read received packet from RX SARM */
                        switch (db->io_mode)
                        {
                                case DM9KS_BYTE_MODE:
                                         for (i=0; i<rx.desc.length; i++)
                                                rdptr[i]=GETB(db->io_data);
                                        break;
                                case DM9KS_WORD_MODE:
                                        tmplen = (rx.desc.length + 1) / 2;
                                        for (i = 0; i < tmplen; i++)
                                                ((u16 *)rdptr)[i] = GETW(db->io_data);
                                        break;
                                case DM9KS_DWORD_MODE:
                                        tmplen = (rx.desc.length + 3) / 4;
                                        for (i = 0; i < tmplen; i++)
                                                ((u32 *)rdptr)[i] = GETL(db->io_data);
                                        break;
                        }
                
                        /* Pass to upper layer */
                        skb->protocol = eth_type_trans(skb,dev);
                        netif_rx(skb);
                        dev->last_rx=jiffies;
                        db->stats.rx_packets++;
                        db->stats.rx_bytes += rx.desc.length;
                        db->cont_rx_pkt_cnt++;
                                
                        if (db->cont_rx_pkt_cnt>=CONT_RX_PKT_CNT)
                        {
                                dmfe_tx_done(0);
                                break;
                        }
                }
                        
        }while((rxbyte & 0x01) == DM9KS_PKT_RDY);
             
}

函数首先读出并保存Memory Data Read Address Register的值。然后有一个空的读操作,具体是为什么要加入这个操作我也不太清楚,希望知道的朋友说一下。在这个读操作后,读取数据端口的第一个字节。根据datasheet,数据包的开始四个字节分别是0x01、status、BYTE_COUNT low和BYTE_COUNT high。读出的第一个字节应该是0x01,为了验证,交给check_rx_ready函数处理,如果不相等,则break,函数退出;若相等则继续对数据包进行处理。

初步验证后,需要继续读取包的前四个字节的信息。由于操作模式不同,需要选择相应的操作模式进行读取。需要注意的是,在该函数的开始的声明部分,有u16 * ptr = (u16*)℞,即ptr是指向无符号16位数的指针,故在读取的时候根据8位、16位或者32位方式,对结果需进行不同的移位处理,具体见源代码。

如上,ptr是指向rx这个变量的指针,rx是rx_t类型的结构

typedef struct _RX_DESC
{
        u8 rxbyte;
        u8 status;
        u16 length;
}RX_DESC;
typedef union{
        u8 buf[4];
        RX_DESC desc;
} rx_t;

RX_DESC刚好四个字节,与网卡数据包头信息长度是一致的。读出的四个字节信息中,位于第二字节的status刚好存放到RX_DESC的status变量中。注意这里的status是RX Status Register中的值,RX Status Register的质地为0x06。所以,在接下来的代码中,程序首先是判断状态,看有没有错误。

若RX Status Register中有一位置位,则说明存在错误,故把标志GoodPacket设置位false,然后再具体检查错误原因。各个位表示的错误信息可以查看datasheet。

上面的检查决定了GoodPacket的值为true或者false,所以如果GoodPacket的值位false,函数直接将不需要的值读出来,然后调用continue,使函数退出do...while循环,开始下一个包的处理,否则,说明是GoodPacket,进行正常的读取操作。

到此,已经确定所接收到的包含有有效信息,可以向内核传送数据了。首先使用函数dev_alloc_skb函数来分配一个skbuff,dev_alloc_skb函数定义于include/linux/skbuff.h中

/**
 *    dev_alloc_skb - allocate an skbuff for receiving
 *    @length: length to allocate
 *
 *    Allocate a new &sk_buff and assign it a usage count of one. The
 *    buffer has unspecified headroom built in. Users should allocate
 *    the headroom they think they need without accounting for the
 *    built in space. The built in space is used for optimisations.
 *
 *    %NULL is returned if there is no free memory. Although this function
 *    allocates memory it can be called from an interrupt.
 */

static inline struct sk_buff *dev_alloc_skb(unsigned int length)
{
    return __dev_alloc_skb(length, GFP_ATOMIC);
}

可见,函数分配了一段内存给用户。分配完成,就开始从网卡读取数据,并将数据传送给上层。代码比较简单,就不在逐一分析了。

-----------------------------To be continued  2008-8-2

由于篇幅限制,下面的部分见:

阅读(249) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~