最新文章专题视频专题问答1问答10问答100问答1000问答2000关键字专题1关键字专题50关键字专题500关键字专题1500TAG最新视频文章推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37视频文章20视频文章30视频文章40视频文章50视频文章60 视频文章70视频文章80视频文章90视频文章100视频文章120视频文章140 视频2关键字专题关键字专题tag2tag3文章专题文章专题2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章专题3
当前位置: 首页 - 正文

2410平台上dm9000a网卡驱动分析

来源:动视网 责编:小OO 时间:2025-09-26 11:28:23
文档

2410平台上dm9000a网卡驱动分析

2410平台上dm9000a网卡驱动分析   该驱动基于linux-2.6.24.4内核。    首先,需要在arch/arm/mach-s3c2410/mach-smdk2410.c文件中添加如下代码:staticstructresources3c_dm9000_resource[]={   [0]={       .start=0x10000000,       .end=0x10000040,       .flags=IORESOURCE_MEM   },   [1]={       
推荐度:
导读2410平台上dm9000a网卡驱动分析   该驱动基于linux-2.6.24.4内核。    首先,需要在arch/arm/mach-s3c2410/mach-smdk2410.c文件中添加如下代码:staticstructresources3c_dm9000_resource[]={   [0]={       .start=0x10000000,       .end=0x10000040,       .flags=IORESOURCE_MEM   },   [1]={       
2410平台上dm9000a网卡驱动分析

    该驱动基于linux-2.6.24.4内核。

 

    首先,需要在arch/arm/mach-s3c2410/mach-smdk2410.c文件中添加如下代码:

static struct resource s3c_dm9000_resource [] = {

    [0] = {

        .start = 0x10000000,

        .end = 0x10000040,

        .flags = IORESOURCE_MEM

    },

    [1] = {

        .start = IRQ_EINT2,

        .end = IRQ_EINT2,

        .flags = IORESOURCE_IRQ,

    }

};

注意上面的start、end等地址是指的网卡的物理地址。然后,还要在该文件中加入如下代码:

struct platform_device s3c_device_dm9000 = {

    .name = "dm9000

    .id = -1,

    .num_resources = ARRAY_SIZE(s3c_dm9000_resource),

    .resource = s3c_dm9000_resource,

};

需要特别注意上面的name字段,当设备驱动程序寻找设别资源时,会根据该字段对设备进行匹配。另外,该文件中的smdk2410_devices[]数组中,还需要加入s3c_device_dm9000,不然系统启动时没有找到该资源就不会调用相应的probe函数。

    下面分析驱动程序的probe函数。若驱动被编译进内核,则在系统启动的时候,该函数会被调用。该函数的源代码如下:

static int dm9k_drv_probe(struct platform_device *pdev)

{

        struct net_device *ndev;

        unsigned long base;

        unsigned int *addr = NULL;

        int ret = -ENODEV;

        ndev = alloc_etherdev(sizeof(struct board_info));

        if (!ndev) {

                printk("%s: could not allocate device.\\n", CARDNAME);

                return -ENOMEM;

        }

        

        ndev->dma = (unsigned char)-1;

        if (pdev->num_resources < 2 || pdev->num_resources > 3) {

                printk("DM9000: Wrong num of resources %d\\n", pdev->num_resources);

                ret = -ENODEV;

                goto out;

        }

        base = pdev->resource[0].start;

        ndev->irq = pdev->resource[1].

start;

        /*

         * Request the regions.

         */

        if (!request_mem_region(base, 4, ndev->name)) {

                ret = -EBUSY;

                goto out;

        }

        addr = ioremap(base, 4);

        if (!addr) {

                ret = -ENOMEM;

                goto release_mem;

        }

        ret = dm9k_probe(ndev, (unsigned long)addr);

        if (ret != 0) {

         iounmap(addr);

 release_mem:

                release_mem_region(base, 4);

 out:

                printk("%s: not found (%d).\\n", CARDNAME, ret);

                kfree(ndev);

        }

        return ret;

}

函数首先调用alloc_etherdev,该函数在include/linux/etherdevice.h中声明,其中有如下语句:

#define alloc_etherdev(sizeof_priv) alloc_etherdev_mq(sizeof_priv, 1)

而alloc_etherdev_mq函数又定义在net/ethernet/eth.c中,如下:

struct net_device *alloc_etherdev_mq(int sizeof_priv, unsigned int queue_count)

{

    return alloc_netdev_mq(sizeof_priv, "eth%d", ether_setup, queue_count);

}

可见,该函数只是用自己的参数来调用alloc_netdev_mq函数。alloc_netdev_mq函数定义在net/core/dev.c中,原型如下:

struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name,

        void (*setup)(struct net_device *), unsigned int queue_count)

关于该函数的说明:

/**

 * alloc_netdev_mq - allocate network device

 * @sizeof_priv: size of private data to allocate space for

 * @name:  device name format string

 * @setup:  callback to initialize device

 * @queue_count: the number of subqueues to allocate

 *

 * Allocates a struct net_device with private data area for driver use

 * and performs basic initialization.  Also allocates subquue structs

 * for each queue on the device at the end of the netdevice.

 */

可见,alloc_etherdev为设备驱动分配了私有数据空间,并对设备驱动做了一些初始化工作。

 

接下来,设备驱动将要检查设备的resources的数量,如果数量小于2或者大于3,则初始化函数自动返回,初始化失败。我们的设备驱动中,resources的数量为2:一个表示设备的IO地址,另一个是设备的中断号。

 

代码

        base = pdev->resource[0].start;

        ndev->irq = pdev->resource[1].

start;

分别得到设备的端口地址和中断号。

接下来,驱动程序将向系统申请io内存,从地址base开始,大小为4个字节。如果申请成功,接下来需要做的就是将地址重新映射,从地址base开始,长度为4个字节。这样做的原因主要是驱动程序一般不直接访问物理地址,而访问虚拟地址。地址重新映射成功后,就调用dm9k_probe函数进行设备初始化。

dm9k_probe函数的全部代码如下

int __init dm9k_probe(struct net_device *dev, unsigned long addr)

{

        struct board_info *db; /* Point a board information structure */

        u32 id_val;

        u16 i, j;

        int retval;

          /* Search for DM9000 serial NIC */

        PUTB(DM9KS_VID_L, addr);

        id_val = GETB(addr + 2); /* Change offset to 2 ^^^^^ */

        PUTB(DM9KS_VID_H, addr);

        id_val |= GETB(addr + 2) << 8;

        PUTB(DM9KS_PID_L, addr);

        id_val |= GETB(addr + 2) << 16;

        PUTB(DM9KS_PID_H, addr);

        id_val |= GETB(addr + 2) << 24;

        if (id_val != DM9KS_ID && id_val != DM9010_ID) {

                /* Dm9k chip not found */

                printk("dmfe_probe(): DM9000 not found. ID=%08X\\n", id_val);

                return -ENODEV;

                }

                

        printk(" I/O: %lx, VID: %x \\n",addr, id_val);

        /* Allocated board information structure */

        memset(dev->priv, 0, sizeof(struct board_info));

        db = (board_info_t *)dev->priv;

        dmfe_dev = dev;

        db->io_addr = addr;

        db->io_data = addr + 2; /* Change offset to 2 ^^^^^ */

        /* driver system function */

                                

        dev->base_addr = addr;

        dev->irq = IRQ_EINT2;

        dev->open = &dmfe_open;

        dev->hard_start_xmit = &dmfe_start_xmit;

        dev->watchdog_timeo = HZ; 

        dev->tx_timeout = dmfe_timeout;

        dev->stop = &dmfe_stop;

        dev->get_stats = &dmfe_get_stats;

        dev->set_multicast_list = &dm9000_hash_table;

        dev->do_ioctl = &dmfe_do_ioctl;

        for(i=0,j=0x10; i<6; i++,j++)

          {

                db->srom[i] = ior(db, j);

               

          }

       

        /* Set Node Address */

        for (i=0; i<6; i++)

                dev->dev_addr[i] = db->srom[i];

        retval = register_netdev(dev);

        if (retval == 0) {

                /* now, print out the card info, in a short format.. */

                printk("%s: at %#lx IRQ %d\\n",

                        dev->name, dev->base_addr, dev->irq);

                if (dev->dma != (unsigned char)-1)

                        printk(" DMA %d\\n", dev->dma);

                if (!is_valid_ether_addr(dev->dev_addr)) {

                        printk("%s: Invalid ethernet MAC address. Please "

                               "set using ifconfig\\n", dev->name);

                } else {

                        /* Print the Ethernet address */

                        printk("%s: Ethernet addr: ", dev->name);

                        for (i = 0; i < 5; i++)

                                printk("%2.2x:", dev->dev_addr[i]);

                        printk("%2.2x\\n", dev->dev_addr[5]);

                }

        }

        return 0;

}

函数首先调用PUTB来写dm9000a芯片,来看看PUTB的实现

#define PUTB(d,a) *((volatile unsigned char *) (a)) = d

可见,PUTB是直接使用的指针,而没有使用内核提供的write等函数,同样,GETB函数如下

#define GETB(a) *((volatile unsigned char *) (a))

注意,这里的地址都是虚拟地址,因为前面调用函数dm9k_probe时传递的addr时重新映射后的,而不是直接传送的物理地址。

具体操作涉及到dm9000a的硬件实现,做简单的说明。dm9000a有两个PORT,一个是INDEX PORT,另一个就是DATA PORT。具体访问哪一个是根据CMD引脚的信号来确定的:CMD为0,则访问INDEX,否则,访问DATA。访问寄存器之前,必须将寄存器的地址存放在INDEX PORT。

首先,驱动程序需要读芯片的ID。DM9000A的ID存放在四个不同的字节中,分别叫做Vendor ID和Product ID。将着四个字节读出来,组合后应该得到0x90000A46,如果读出来的ID与该值不相等,说明不是DM9000A网卡,程序将返回,初始化失败。

读出ID相同后,就可以认为系统中存在dm9000a网卡了,接下来就开始进行其他初始化工作。主要工作

        dev->base_addr = addr;

        dev->irq = IRQ_EINT2;

        dev->open = &dmfe_open;

        dev->hard_start_xmit = &dmfe_start_xmit;

        dev->watchdog_timeo = HZ; 

        dev->tx_timeout = dmfe_timeout;

        dev->stop = &dmfe_stop;

        dev->get_stats = &dmfe_get_stats;

        dev->set_multicast_list = &dm9000_hash_table;

        dev->do_ioctl = &dmfe_do_ioctl;

就是为net_device的成员指定功能函数,以便系统需要的时候进行调用。完成这些基本的工作后,就可以向系统注册设备了

retval = register_netdev(dev);

注册完成,该函数就返回。probe函数剩下的就是对返回值的判断了,若注册成功,直接推出,probe完成;失败的话,还需要将ioremap过的地方ioumap掉,request_mem_region的地方release掉。

前面分析了dm9000a网卡的probe部分,接下来继续其他部分。

 

当用户在命令行下使用ifconfig等命令的时候,网卡设备将打开,系统将调用open函数。dm9000a的open函数如下

static int dmfe_open(struct net_device *dev)

{

        board_info_t *db = (board_info_t *)dev->priv;

        u8 reg_nsr;

        int i;

       

        if (request_irq(dev->irq,&dmfe_interrupt,IRQF_SHARED,dev->name,dev)) 

                return -EAGAIN;

        /* Grab the IRQ */

        set_irq_type(dev->irq, IRQ_TYPE_EDGE_RISING);

        /* Initilize DM910X board */

        dmfe_init_dm9000(dev);

 

        /* Init driver variable */

        db->reset_counter = 0;

        db->reset_tx_timeout = 0;

        db->cont_rx_pkt_cnt = 0;

        

        /* check link state and media speed */

        db->Speed =10;

        i=0;

        do {

                reg_nsr = ior(db,0x1);

                if(reg_nsr & 0x40) /* link OK!! */

                {

                        /* wait for detected Speed */

                        mdelay(200);

                        reg_nsr = ior(db,0x1);

                        if(reg_nsr & 0x80)

                                db->Speed =10;

                        else

                                db->Speed =100;

                        break;

                }

                i++;

                mdelay(1);

        }while(i<3000); /* wait 3 second */

        //printk("i=%d Speed=%d\\n",i,db->Speed); 

        /* set and active a timer process */

        init_timer(&db->timer);

        db->timer.expires = DMFE_TIMER_WUT * 2;

        db->timer.data = (unsigned long)dev;

        db->timer.function = &dmfe_timer;

        add_timer(&db->timer); //Move to DM9000 initiallization was finished.

         

        netif_start_queue(dev);

        return 0;

}

函数首先向系统申请中断,利用内核提供的request_irq函数。该函数声明于include/linux/interrupt.h中,而它的定于位于kernel/irq/manage.c中。关于它的说明和原型如下

/**

 *    request_irq - allocate an interrupt line

 *    @irq: Interrupt line to allocate

 *    @handler: Function to be called when the IRQ occurs

 *    @irqflags: Interrupt type flags

 *    @devname: An ascii name for the claiming device

 *    @dev_id: A cookie passed back to the handler function

 *

 *    This call allocates interrupt resources and enables the

 *    interrupt line and IRQ handling. From the point this

 *    call is made your handler function may be invoked. Since

 *    your handler function must clear any interrupt the board

 *    raises, you must take care both to initialise your hardware

 *    and to set up the interrupt handler in the right order.

 *

 *    Dev_id must be globally unique. Normally the address of the

 *    device data structure is used as the cookie. Since the handler

 *    receives this value it makes sense to use it.

 *

 *    If your interrupt is shared you must pass a non NULL dev_id

 *    as this is required when freeing the interrupt.

 *

 *    Flags:

 *

 *    IRQF_SHARED        Interrupt is shared

 *    IRQF_DISABLED    Disable local interrupts while processing

 *    IRQF_SAMPLE_RANDOM    The interrupt can be used for entropy

 *

 */

int request_irq(unsigned int irq, irq_handler_t handler,

        unsigned long irqflags, const char *devname, void *dev_id)

可见,传递给该函数的第一个参数是申请的中断号;第二个参数是一个函数指针,当发生相应的中断时,系统将调用该函数处理中断;第三个参数是中断的标志,可以是它说明文档中提到的三种中的任何一种,我们用的是IRQF_SHARED,表示中断可以共享;第四个参数就是设备的简称,可以在/proc/interrupts列表中找到。

申请完成中断后,驱动程序设置了中断的类型,使用set_irq_type函数。该函数声明于include/linux/interrupt.h,而定义于kernel/irq/chip.c,原型如下

/**

 *    set_irq_type - set the irq type for an irq

 *    @irq:    irq number

 *    @type:    interrupt type - see include/linux/interrupt.h

 */

int set_irq_type(unsigned int irq, unsigned int type)

第一个参数是中断号,第二歌参数是中断类型,表示上升沿触发、下降沿触发、高电平触发或者低电平触发。我们使用的是IRQ_TYPE_EDGE_RISING,即上升沿触发。

中断设置完成后,驱动程序需要对dm9000a芯片进行初始化,调用dmfe_init_dm9000函数,如下

static void dmfe_init_dm9000(struct net_device *dev)

{

        board_info_t *db = (board_info_t *)dev->priv;

       

        /* set the internal PHY power-on, GPIOs normal, and wait 2ms */

        iow(db, DM9KS_GPR, 1); /* Power-Down PHY */

        udelay(500);

        iow(db, DM9KS_GPR, 0); /* GPR (reg_1Fh)bit GPIO0=0 pre-activate PHY */

        udelay(20); /* wait 2ms for PHY power-on ready */

        /* do a software reset and wait 20us */

        iow(db, DM9KS_NCR, 3);

        udelay(20); /* wait 20us at least for software reset ok */

        iow(db, DM9KS_NCR, 3); /* NCR (reg_00h) bit[0] RST=1 & Loopback=1, reset on */

        udelay(20); /* wait 20us at least for software reset ok */

        /* I/O mode */

        db->io_mode = ior(db, DM9KS_ISR) >> 6; /* ISR bit7:6 keeps I/O mode */

        /* Set PHY */

        db->op_mode = media_mode;

        set_PHY_mode(db);

        /* Program operating register */

        iow(db, DM9KS_NCR, 0);

        iow(db, DM9KS_TCR, 0); /* TX Polling clear */

        iow(db, DM9KS_BPTR, 0x3f); /* Less 3kb, 600us */

        iow(db, DM9KS_SMCR, 0); /* Special Mode */

        iow(db, DM9KS_NSR, 0x2c); /* clear TX status */

        iow(db, DM9KS_ISR, 0x0f); /* Clear interrupt status */

        /* Added by jackal at 03/29/2004 */

 

        /* Set address filter table */

        dm9000_hash_table(dev);

        /* Activate DM9000A/DM9010 */

        iow(db, DM9KS_RXCR, DM9KS_REG05 | 1); /* RX enable */

        iow(db, DM9KS_IMR, DM9KS_REGFF); // Enable TX/RX interrupt mask

 

        /* Init Driver variable */

        db->tx_pkt_cnt = 0;

                

        netif_carrier_on(dev);

        spin_lock_init(&db->lock);

}

该函数中使用了iow函数,看一看它的实现

static void iow(board_info_t *db, int reg, u8 value)

{

        PUTB(reg, db->io_addr);

        PUTB(value, db->io_data);

}

即将value写入reg表示的寄存器中。dmfe_init_dm9000函数的具体功能函数里面已经有注释,更详细的可以查看datasheet。dmfe_init_dm9000函数返回后,open函数还做了一些工作。首先,初始化一些设备变量

        db->reset_counter = 0;

        db->reset_tx_timeout = 0;

        db->cont_rx_pkt_cnt = 0;

这些值在发送和接收数据的时候将会使用到,到讨论那些函数的时候将详细介绍。接下来,驱动程序需要为自己增加一个timer

        init_timer(&db->timer);

        db->timer.expires = DMFE_TIMER_WUT * 2;

        db->timer.data = (unsigned long)dev;

        db->timer.function = &dmfe_timer;

        add_timer(&db->timer); //Move to DM9000 initiallization was finished.

timer的expires变量决定定时时间,当定时时间到的时候,就会执行function指定的函数。最后,使用add_timer()函数将初始化的timer插入挂起定时器全局队列。关于function指定的函数,将在后面说明。

最后,open函数调用netif_start_queue,该函数的定义位于include/linux/netdevice.h中

/**

 *    netif_start_queue - allow transmit

 *    @dev: network device

 *

 *    Allow upper layers to call the device hard_start_xmit routine.

 */

static inline void netif_start_queue(struct net_device *dev)

{

    clear_bit(__LINK_STATE_XOFF, &dev->state);

}

可见,该函数告诉系统可以使用hard_start_xmit进行数据发送了。

open函数到此就结束了。

前面讨论了probe函数和open函数,下面继续。

 

内核发送数据在底层是通过dmfe_start_xmit函数来实现的

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

{

        board_info_t *db = (board_info_t *)dev->priv;

        char * data_ptr;

        int i, tmplen;

        if(db->Speed == 10)

                {if (db->tx_pkt_cnt >= 1) return 1;}

        else

                {if (db->tx_pkt_cnt >= 2) return 1;}

        

        /* packet counting */

        db->tx_pkt_cnt++;

        db->stats.tx_packets++;

        db->stats.tx_bytes+=skb->len;

        if (db->Speed == 10)

                {if (db->tx_pkt_cnt >= 1) netif_stop_queue(dev);}

        else

                {if (db->tx_pkt_cnt >= 2) netif_stop_queue(dev);}

        /* Disable all interrupt */

        iow(db, DM9KS_IMR, DM9KS_DISINTR);

        /* Set TX length to reg. 0xfc & 0xfd */

        iow(db, DM9KS_TXPLL, (skb->len & 0xff));

        iow(db, DM9KS_TXPLH, (skb->len >> 8) & 0xff);

        /* Move data to TX SRAM */

        data_ptr = (char *)skb->data;

        

        PUTB(DM9KS_MWCMD, db->io_addr); // Write data into SRAM trigger

        switch(db->io_mode)

        {

                case DM9KS_BYTE_MODE:

                        for (i = 0; i < skb->len; i++)

                                PUTB((data_ptr[i] & 0xff), db->io_data);

                        break;

                case DM9KS_WORD_MODE:

                        tmplen = (skb->len + 1) / 2;

                        for (i = 0; i < tmplen; i++)

                                 PUTW(((u16 *)data_ptr)[i], db->io_data);

                         break;

                 case DM9KS_DWORD_MODE:

                         tmplen = (skb->len + 3) / 4; 

                        for (i = 0; i< tmplen; i++)

                                PUTL(((u32 *)data_ptr)[i],

 db->io_data);

                        break;

        }

        

#if !defined(ETRANS)

        /* Issue TX polling command */

        iow(db, DM9KS_TCR, 0x1); /* Cleared after TX complete*/

#endif

        /* Saved the time stamp */

        dev->trans_start = jiffies;

        db->cont_rx_pkt_cnt =0;

        /* Free this SKB */

        dev_kfree_skb(skb);

        /* Re-enable interrupt */

        iow(db, DM9KS_IMR, DM9KS_REGFF);

        return 0;

}

该函数首先判断设备使用的模式,若speed为10,则发送的数据包个数最大为1,若speed为100,则最大个数为2.超过这两个值,函数立即返回。若不超过,则说明可以进行数据发送。然后是更新系统的统计信息。如果待发送包达到上限,则调用netif_stop_queue,告诉内核暂时停止内核与驱动程序间的数据传递。

这些完成后,就可以开始真正的数据传输了。先禁止dm9000a的所有中断,通过写它的Interrupt Mask Register来实现。然后将要传递的数据的长度信息写入TX Packet Length Register中。

需要注意的是char * data_ptr在这里不能理解为一个指向char变量的指针,而应该理解为一个char数组。根据芯片的硬件连接方式,选择字节、半字或者字的方式对数据进行发送。发送完成后,记录下时间戳,并释放skb的空间,然后允许dm9000a的中断,以便继续进行发送或者接收。

stop方法和open函数的方法作用相反,即停止网络设备

static int dmfe_stop(struct net_device *dev)

{

        board_info_t *db = (board_info_t *)dev->priv;

        

        /* deleted timer */

        del_timer(&db->timer);

        netif_stop_queue(dev); 

        /* free interrupt */

        free_irq(dev->irq, dev);

        /* RESET devie */

        phy_write(db, 0x00, 0x8000); /* PHY RESET */

        iow(db, DM9KS_GPR, 0x01); /* Power-Down PHY */

        iow(db, DM9KS_IMR, DM9KS_DISINTR); /* Disable all interrupt */

        iow(db, DM9KS_RXCR, 0x00); /* Disable RX */

        /* Dump Statistic counter */

        return 0;

}

完成的工作主要有删除定时器、释放中断、调用netif_stop_queue()告诉内核停止内核与驱动程序之间的数据交换,最后使dm9000a网卡处于power-down模式。

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

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*)℞

        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                                                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                                                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);

}

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

上面已经分析到中断处理的接收中断处理函数,下面分析发送中断处理函数。

 

在前面,我们说到系统调用dmfe_start_xmit来进行数据发送,当数据发送完成后,将产生一个数据发送中断,由interrupt函数检测到该中断后,将调用dmfe_tx_done()进行处理

static void dmfe_tx_done(unsigned long unused)

{

        struct net_device *dev = dmfe_dev;

        board_info_t *db = (board_info_t *)dev->priv;

        int nsr;

               

        nsr = ior(db, DM9KS_NSR);

        if(nsr & 0x04) db->tx_pkt_cnt--;

        if(nsr & 0x08) db->tx_pkt_cnt--;

        if (db->tx_pkt_cnt < 0)

        {

                printk("[dmfe_tx_done] tx_pkt_cnt ERROR!!\\n"

);

                db->tx_pkt_cnt =0;

        }

        if (db->Speed == 10)

                {if(db->tx_pkt_cnt < 1 ) netif_wake_queue(dev);}

        else 

                {if(db->tx_pkt_cnt < 2 ) netif_wake_queue(dev);}

        

        return;

}

该函数主要完成两个动作:一是把tx_pkt_cnt成员的值减少1,表示已经完成一个数据包的发送了;二是判断tx_pkt_cnt的值,根据speed的值,判断是否可以调用netif_wake_queue()函数来通知内核网卡可以接受新的数据发送任务了。

 

文档

2410平台上dm9000a网卡驱动分析

2410平台上dm9000a网卡驱动分析   该驱动基于linux-2.6.24.4内核。    首先,需要在arch/arm/mach-s3c2410/mach-smdk2410.c文件中添加如下代码:staticstructresources3c_dm9000_resource[]={   [0]={       .start=0x10000000,       .end=0x10000040,       .flags=IORESOURCE_MEM   },   [1]={       
推荐度:
  • 热门焦点

最新推荐

猜你喜欢

热门推荐

专题
Top