mini2440 dm9000 网卡驱动详解 2

 

3. platform_driver的remove, suspend和resume的实现

       remove函数的功能是把设备从内核中移除,释放内存区域。该函数在卸载模块时被调用。代码清单如下:

 

static int __devexit    
dm9000_drv_remove(struct platform_device *pdev)    
{    
    struct net_device *ndev = platform_get_drvdata(pdev);    
   
    platform_set_drvdata(pdev, NULL);    
   
    unregister_netdev(ndev);    
    dm9000_release_board(pdev, (board_info_t *) netdev_priv(ndev));    
    free_netdev(ndev);      /* free device structure */   
   
    dev_dbg(&pdev->dev, "released and freed device\n");    
    return 0;    
}  

       suspend函数并不真正把设备从内核中移除,而只是标志设备为removed状态,并设置挂起标志位,最后关闭设备。代码清单如下:

 

static int dm9000_drv_suspend(struct platform_device *dev, pm_message_t state)    
{    
    struct net_device *ndev = platform_get_drvdata(dev);    
    board_info_t *db;    
   
    if (ndev) {    
        db = netdev_priv(ndev);    
        db->in_suspend = 1;    
   
        if (netif_running(ndev)) {    
            netif_device_detach(ndev);    
            dm9000_shutdown(ndev);    
        }    
    }    
    return 0;    
}  

     resume函数将挂起的设备复位并初始化,软后将设备标志为attached状态,并设置挂起标志位。代码清单如下:

 

static int dm9000_drv_resume(structplatform_device *dev)   

{   

   struct net_device *ndev = platform_get_drvdata(dev);   

   board_info_t *db = netdev_priv(ndev);   

  

   if (ndev) {   

  

       if (netif_running(ndev)) {   

           dm9000_reset(db);   

           dm9000_init_dm9000(ndev);   

  

           netif_device_attach(ndev);   

       }   

  

       db->in_suspend = 0;   

   }   

   return 0;   

} 

4. 下面看一下用于填充net_device中netdev_ops和ethtool_ops的一些函数。

    代码在上面已经写出来了,为了看着方便在下面再写一遍,可以看出虽然mini2440的板子上没有为DM9000挂EEPROM,但这里还是定义了操作EEPROM的函数。就是说写驱动的时候是不考虑具体的板子的,你板子用不用是你的事,但是我们的驱动应该所有的功能都考虑进去。这也体现了驱动和平台分离的设计思想。

 

 

static const struct net_device_ops dm9000_netdev_ops = {    
    .ndo_open       = dm9000_open,    
    .ndo_stop       = dm9000_stop,    
    .ndo_start_xmit     = dm9000_start_xmit,    
    .ndo_tx_timeout     = dm9000_timeout,    
    .ndo_set_multicast_list = dm9000_hash_table,    
    .ndo_do_ioctl       = dm9000_ioctl,    
    .ndo_change_mtu     = eth_change_mtu,    
    .ndo_validate_addr  = eth_validate_addr,    
    .ndo_set_mac_address    = eth_mac_addr,   
#ifdef CONFIG_NET_POLL_CONTROLLER    
    .ndo_poll_controller    = dm9000_poll_controller,   
#endif    
};  


 
static const struct ethtool_ops dm9000_ethtool_ops = {    
    .get_drvinfo        = dm9000_get_drvinfo,    
    .get_settings       = dm9000_get_settings,    
    .set_settings       = dm9000_set_settings,    
    .get_msglevel       = dm9000_get_msglevel,    
    .set_msglevel       = dm9000_set_msglevel,    
    .nway_reset     = dm9000_nway_reset,    
    .get_link       = dm9000_get_link,    
    .get_eeprom_len     = dm9000_get_eeprom_len,    
    .get_eeprom     = dm9000_get_eeprom,    
    .set_eeprom     = dm9000_set_eeprom,    
};  

  *dm9000_open()

 

    进行的工作有 向内核注册中断,复位并初始化dm9000,检查MII接口,使能传输等。代码清单如下:

 

/*   
 *  Open the interface.   
 *  The interface is opened whenever "ifconfig" actives it.   
 */   
static int   
dm9000_open(struct net_device *dev)    
{    
    board_info_t *db = netdev_priv(dev);    
    unsigned long irqflags = db->irq_res->flags & IRQF_TRIGGER_MASK;    
   
    if (netif_msg_ifup(db))    
        dev_dbg(db->dev, "enabling %s\n", dev->name);    
   
    /* If there is no IRQ type specified, default to something that   
     * may work, and tell the user that this is a problem */   
   
    if (irqflags == IRQF_TRIGGER_NONE)    
        dev_warn(db->dev, "WARNING: no IRQ resource flags set.\n");    
   
    irqflags |= IRQF_SHARED;    
   
    if (request_irq(dev->irq, &dm9000_interrupt, irqflags, dev->name, dev))/*注册一个中断,中断处理函数为dm9000_interrupt()*/   
        return -EAGAIN;    
   
    /* Initialize DM9000 board */   
    dm9000_reset(db);    
    dm9000_init_dm9000(dev);    
   
    /* Init driver variable */   
    db->dbug_cnt = 0;    
   
    mii_check_media(&db->mii, netif_msg_link(db), 1);    
    netif_start_queue(dev);    
        
    dm9000_schedule_poll(db);/*之前在probe函数中已经使用INIT_DELAYED_WORK来初始化一个延迟工作队列并关联了一个操作函数dm9000_poll_work(), 此时运行schedule来调用这个函数*/   
   
    return 0;    
}  

*dm9000_stop()

     做的工作基本上和open相反。代码清单如下:

 

/*   
 * Stop the interface.   
 * The interface is stopped when it is brought.   
 */   
static int   
dm9000_stop(struct net_device *ndev)    
{    
    board_info_t *db = netdev_priv(ndev);    
   
    if (netif_msg_ifdown(db))    
        dev_dbg(db->dev, "shutting down %s\n", ndev->name);    
   
    cancel_delayed_work_sync(&db->phy_poll); /*杀死延迟工作队列phy_poll*/   
   
        /*停止传输并清空carrier*/   
    netif_stop_queue(ndev);    
    netif_carrier_off(ndev);    
   
    /* free interrupt */   
    free_irq(ndev->irq, ndev);    
   
    dm9000_shutdown(ndev);    
   
    return 0;    
}  


   *dm9000_start_xmit()

 

    重要的发送数据包函数。从上层发送sk_buff包。在看代码之前先来看一下DM9000是如何发送数据包的。


如上图所示,在DM9000内部SRAM中,地址0x0000~0x0BFF是TX Buffer,地址0x0C00~0x3FFF是RX Buffer。在发送一个包之前,包中的有效数据必须先被存储到TX Buffer中并且使用输出端口命令来选择MWCMD寄存器。包的长度定义在TXPLL和TXPLH中。最后设置TXCR寄存器的bit[0] TXREQ来自动发送包。如果设置了IMR寄存器的PTM位,则DM9000会产生一个中断触发在ISR寄存器的bit[1]=PTS=1, 同时设置一个完成标志在NSR寄存器的bit[2]=TX1END或者 bit[3]=TX2END,表示包已经发送完了。发送一个包的具体步骤如下:

 

Step 1: 检查存储数据宽度。通过读取中断状态寄存器(ISR)的bit[7:6]来确定是8bit,16bit还是32bit。

 

Step 2: 写数据到TX SRAM中。

 

Step 3: 写传输长度到TXPLL和TXPLH寄存器中。

 

Step 4: 设置TXCR寄存器的bit[0]TXREQ来开始发送一个包。

 

代码清单如下,让我们看看在获得自旋锁这段期间都干了些什么:

 

/*   
 *  Hardware start transmission.   
 *  Send a packet to media from the upper layer.   
 */   
static int   
dm9000_start_xmit(struct sk_buff *skb, struct net_device *dev)    
{    
    unsigned long flags;    
    board_info_t *db = netdev_priv(dev);    
   
    dm9000_dbg(db, 3, "%s:\n", __func__);    
   
    if (db->tx_pkt_cnt > 1)    
        return NETDEV_TX_BUSY;    
   
        /*获得自旋锁*/   
    spin_lock_irqsave(&db->lock, flags);     
   
    /* Move data to DM9000 TX RAM */   
        /*下面四行代码将skb中的data部分写入DM9000的TX RAM,并更新已发送字节数和发送计数*/   
    writeb(DM9000_MWCMD, db->io_addr);    
   
    (db->outblk)(db->io_data, skb->data, skb->len);    
    dev->stats.tx_bytes += skb->len;    
   
    db->tx_pkt_cnt++;    
    /* TX control: First packet immediately send, second packet queue */   
        /*如果发送的是第一个包,则设置一下包的长度后直接发送*/   
        /*如果发的不是第一个包,*/   
    if (db->tx_pkt_cnt == 1) {    
        /* Set TX length to DM9000 */   
        iow(db, DM9000_TXPLL, skb->len);    
        iow(db, DM9000_TXPLH, skb->len >> 8);    
   
        /* Issue TX polling command */   
        iow(db, DM9000_TCR, TCR_TXREQ); /* Cleared after TX complete */   
   
        dev->trans_start = jiffies;  /* save the time stamp */   
    } else {    
        /* Second packet */   
                /*如果发送的是第二个数据包(表明队列中此时有包发送),则将其加入队列中:将skb->len和skb->ip_summed(控制校验操作)赋值给board_info_t中有关队列的相关成员。调用函数netif_stop_queue(dev),通知内核现在queue已满,不能再将发送数据传到队列中,注:第二个包的发送将在tx_done中实现。*/   
        db->queue_pkt_len = skb->len;    
        netif_stop_queue(dev);    
    }    
   
        /*释放自旋锁*/   
    spin_unlock_irqrestore(&db->lock, flags);    
   
    /* free this SKB */   
    dev_kfree_skb(skb);    
   
    return 0;    
}  

*dm9000_timeout()

    当watchdog超时时调用该函数。主要的功能是保存寄存器地址,停止队列,重启并初始化DM9000,唤醒队列,恢复寄存器地址。

 

    代码清单如下:

/* Our watchdog timed out. Called by the networking layer */   
static void dm9000_timeout(struct net_device *dev)    
{    
    board_info_t *db = netdev_priv(dev);    
    u8 reg_save;    
    unsigned long flags;    
   
    /* Save previous register address */   
    reg_save = readb(db->io_addr);    
    spin_lock_irqsave(&db->lock, flags);    
   
    netif_stop_queue(dev);    
    dm9000_reset(db);    
    dm9000_init_dm9000(dev);    
    /* We can accept TX packets again */   
    dev->trans_start = jiffies;    
    netif_wake_queue(dev);    
   
    /* Restore previous register address */   
    writeb(reg_save, db->io_addr);    
    spin_unlock_irqrestore(&db->lock, flags);    
}  

   *dm9000_hash_table()

 

    该函数用来设置DM9000的组播地址。代码清单如下:

/*   
 *  Set DM9000 multicast address   
 */   
static void   
dm9000_hash_table(struct net_device *dev)    
{    
    board_info_t *db = netdev_priv(dev);    
    struct dev_mc_list *mcptr = dev->mc_list;    
    int mc_cnt = dev->mc_count;    
    int i, oft;    
    u32 hash_val;    
    u16 hash_table[4];    
    u8 rcr = RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN;    
    unsigned long flags;    
   
    dm9000_dbg(db, 1, "entering %s\n", __func__);    
   
    spin_lock_irqsave(&db->lock, flags);    
   
    for (i = 0, oft = DM9000_PAR; i < 6; i++, oft++)    
        iow(db, oft, dev->dev_addr[i]);    
   
    /* Clear Hash Table */   
    for (i = 0; i < 4; i++)    
        hash_table[i] = 0x0;    
   
    /* broadcast address */   
    hash_table[3] = 0x8000;    
   
    if (dev->flags & IFF_PROMISC)    
        rcr |= RCR_PRMSC;    
   
    if (dev->flags & IFF_ALLMULTI)    
        rcr |= RCR_ALL;    
   
    /* the multicast address in Hash Table : 64 bits */   
    for (i = 0; i < mc_cnt; i++, mcptr = mcptr->next) {    
        hash_val = ether_crc_le(6, mcptr->dmi_addr) & 0x3f;    
        hash_table[hash_val / 16] |= (u16) 1 << (hash_val % 16);    
    }    
   
    /* Write the hash table to MAC MD table */   
    for (i = 0, oft = DM9000_MAR; i < 4; i++) {    
        iow(db, oft++, hash_table[i]);    
        iow(db, oft++, hash_table[i] >> 8);    
    }    
   
    iow(db, DM9000_RCR, rcr);    
    spin_unlock_irqrestore(&db->lock, flags);    
}  


   *dm9000_ioctl()

 

    从源码可以看出,dm9000的ioctl实际上是使用了mii的ioctl。代码清单如下:

static int dm9000_ioctl(struct net_device *dev, struct ifreq *req, int cmd)    
{    
    board_info_t *dm = to_dm9000_board(dev);    
   
    if (!netif_running(dev))    
        return -EINVAL;    
   
    return generic_mii_ioctl(&dm->mii, if_mii(req), cmd, NULL);    
}  

    *dm9000_poll_controller()

    当内核配置Netconsole时该函数生效。代码清单如下:

#ifdef CONFIG_NET_POLL_CONTROLLER    
/*   
 *Used by netconsole   
 */   
static void dm9000_poll_controller(struct net_device *dev)    
{    
    disable_irq(dev->irq);    
    dm9000_interrupt(dev->irq, dev);    
    enable_irq(dev->irq);    
}   
#endif  



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值