企业信息

    深圳市爱欣文科技有限公司

  • 11
  • 公司认证: 营业执照已认证
  • 企业性质:私营企业
    成立时间:2005
  • 公司地址: 湖北省 武汉市 武汉市东湖开发区珞瑜路727号光谷银座1305室
  • 姓名: 李先生
  • 认证: 手机未认证 身份证未认证 微信未绑定

    供应分类

2410平台上dm9000a网卡驱动分析

时间:2014-04-08点击次数:91

该驱动基于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("<DM9KS> 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时重新映射后的,而不是直接传送的物理地址。

 


http://davicom1.cn.b2b168.com