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