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