DM9000网卡驱动深度分析

一、dm9000_porbe函数分析
不同于u-boot代码,tq2440中的DM9000更加复杂,需要分析的点也很多:
  1. /*
  2.  * Search DM9000 board, allocate space and register it
  3.  */
  4. static int __devinit
  5. dm9000_probe(struct platform_device *pdev)
  6. {
  7.     struct dm9000_plat_data *pdata = pdev->dev.platform_data;
  8.     struct board_info *db;    /* Point a board information structure */
  9.     struct net_device *ndev;
  10.     const unsigned char *mac_src;
  11.     int ret = 0;
  12.     int iosize;
  13.     int i;
  14.     u32 id_val;

  15. #if defined(CONFIG_ARCH_S3C2410)
  16.     unsigned int oldval_bwscon = *(volatile unsigned int *)S3C2410_BWSCON;
  17.     unsigned int oldval_bankcon4 = *(volatile unsigned int *)S3C2410_BANKCON4;
  18. #endif

  19.     /* Init network device */
  20.     ndev = alloc_etherdev(sizeof(struct board_info));                                     //分配netdev结构
  21.     if (!ndev) {
  22.         dev_err(&pdev->dev, "could not allocate device. ");
  23.         return -ENOMEM;
  24.     }

  25.     SET_NETDEV_DEV(ndev, &pdev->dev);

  26.     dev_dbg(&pdev->dev, "dm9000_probe() ");                     //不管

  27. #if defined(CONFIG_ARCH_S3C2410)                                 //2410可以不管
  28. //    *((volatile unsigned int *)S3C2410_BWSCON) = (oldval_bwscon & ~(3<<16)) | S3C2410_BWSCON_DW4_16 | S3C2410_BWSCON_WS4 | S3C2410_BWSCON_ST4;
  29.     *((volatile unsigned int *)S3C2410_BWSCON) = (oldval_bwscon & ~(3<<16)) | S3C2410_BWSCON_DW4_16 ;
  30.     *((volatile unsigned int *)S3C2410_BANKCON4) = 0x1f7c;
  31. #endif

  32.     /* setup board info structure */
  33.     db = netdev_priv(ndev);
  34.     memset(db, 0, sizeof(*db));

  35.     db->dev = &pdev->dev;
  36.     db->ndev = ndev;

  37.     spin_lock_init(&db->lock);
  38.     mutex_init(&db->addr_lock);

  39.     INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work);

  40.     db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);                  //获取平台资源,对应的platform_driver
  41.     db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
  42.     db->irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
  43.    
  44.    ..................
  45. }
从这里可以引出之前的知识点:平台设备和平台驱动的相关知识。在dm9000.c中有:
  1. static struct platform_driver dm9000_driver = {
  2.     .driver    = {
  3.         .name = "dm9000",                                                //这个平台驱动通过name和tq2440中的dm9000的驱动设备结构对接
  4.         .owner     = THIS_MODULE,
  5.     },
  6.     .probe = dm9000_probe,
  7.     .remove = __devexit_p(dm9000_drv_remove),
  8.     .suspend = dm9000_drv_suspend,
  9.     .resume = dm9000_drv_resume,
  10. };
在arch/arm/mach-tq2440.c:
  1. struct platform_device s3c_device_dm9000 = {
  2.     .name        = "dm9000",                                          //这里有同样的name来实现
  3.     .id            = 0,
  4.     .num_resources    = ARRAY_SIZE(s3c_dm9k_resource),
  5.     .resource        = s3c_dm9k_resource,                             //这里保存着dm9000硬件的资源
  6.     .dev            = {
  7.         .platform_data = &s3c_dm9k_platdata,
  8.     }
  9. };
同样在此文件中:
  1. static struct resource s3c_dm9k_resource[] = {
  2.     [0] = {                                               //用的片选4
  3.         .start    = S3C2410_CS4,                          //这里的起始地址是0x20000000和网上一些0x20000300不一样。wu'suo
  4.         .end    = S3C2410_CS4 + 3,                        //.end为什么尾地址是+3?因为一个地址需要四个字节。这是根据网卡来定义的
  5.         .flags    = IORESOURCE_MEM,
  6.     },
  7.     [1] = {                                               //0x20000004~0x20000007?
  8.         .start    = S3C2410_CS4 + 4,                      //这里的4意味着地址线2等于1,设置了cmd为高位,是数据内容
  9.         .end    = S3C2410_CS4 + 4 + 3,
  10.         .flags    = IORESOURCE_MEM,
  11.     },
  12.     [2] = {
  13.         .start    = IRQ_EINT7,                            //对应的中断信息
  14.         .end    = IRQ_EINT7,
  15.         .flags    = IORESOURCE_IRQ | IRQF_TRIGGER_RISING,
  16.     }

  17. };
回来继续看dm9000.c中的probe函数:
  1.     db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  2.     db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
  3.     db->irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);

  4.     if (db->addr_res == NULL || db->data_res == NULL ||
  5.      db->irq_res == NULL) {
  6.         dev_err(db->dev, "insufficient resources ");
  7.         ret = -ENOENT;
  8.         goto out;
  9.     }

  10.     iosize = res_size(db->addr_res);
  11.     db->addr_req = request_mem_region(db->addr_res->start, iosize,
  12.                      pdev->name);

  13.     if (db->addr_req == NULL) {
  14.         dev_err(db->dev, "cannot claim address reg area ");
  15.         ret = -EIO;
  16.         goto out;
  17.     }

  18.     db->io_addr = ioremap(db->addr_res->start, iosize);                    //将地址信息重映射

  19.     if (db->io_addr == NULL) {
  20.         dev_err(db->dev, "failed to ioremap address reg ");
  21.         ret = -EINVAL;
  22.         goto out;
  23.     }

  24.     iosize = res_size(db->data_res);
  25.     db->data_req = request_mem_region(db->data_res->start, iosize,
  26.                      pdev->name);

  27.     if (db->data_req == NULL) {
  28.         dev_err(db->dev, "cannot claim data reg area ");
  29.         ret = -EIO;
  30.         goto out;
  31.     }

  32.     db->io_data = ioremap(db->data_res->start, iosize);               //将数据信息重映射

  33.     if (db->io_data == NULL) {
  34.         dev_err(db->dev, "failed to ioremap data reg ");
  35.         ret = -EINVAL;
  36.         goto out;
  37.     }

  38.     .........................

  39.     id_val = ior(db, DM9000_CHIPR);                                    //读取芯片信息
  40. //    dev_dbg(db->dev, "dm9000 revision 0x%02x io_mode %02x ", id_val, db->io_mode);
  41.     printk(KERN_INFO "dm9000 revision 0x%02x io_mode %02x ", id_val, db->io_mode);

  42.     switch (id_val) {
  43.     case CHIPR_DM9000A:
  44.         db->type = TYPE_DM9000A;
  45.         break;
  46.     case CHIPR_DM9000B:
  47.         db->type = TYPE_DM9000B;
  48.         break;
  49.     case CHIPR_DM9000C:
  50.         db->type = TYPE_DM9000C;
  51.         break;
  52.     default:
  53.         dev_dbg(db->dev, "ID %02x => defaulting to DM9000E ", id_val);
  54.         db->type = TYPE_DM9000E;
  55.     }

  56.     /* from this point we assume that we have found a DM9000 */

  57.     /* driver system function */                                                //下面在设置dm9000的操作函数集了
  58.     ether_setup(ndev);

  59.     ndev->open         = &dm9000_open;
  60.     ndev->hard_start_xmit = &dm9000_start_xmit;
  61.     ndev->tx_timeout = &dm9000_timeout;
  62.     ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
  63.     ndev->stop         = &dm9000_stop;
  64.     ndev->set_multicast_list = &dm9000_hash_table;
  65.     ndev->ethtool_ops     = &dm9000_ethtool_ops;
  66.     ndev->do_ioctl         = &dm9000_ioctl;

  67. #ifdef CONFIG_NET_POLL_CONTROLLER
  68.     ndev->poll_controller     = &dm9000_poll_controller;
  69. #endif

  70.     db->msg_enable = NETIF_MSG_LINK;
  71.     db->mii.phy_id_mask = 0x1f;
  72.     db->mii.reg_num_mask = 0x1f;
  73.     db->mii.force_media = 0;
  74.     db->mii.full_duplex = 0;
  75.     db->mii.dev     = ndev;
  76.     db->mii.mdio_read = dm9000_phy_read;
  77.     db->mii.mdio_write = dm9000_phy_write;

  78. #if defined(CONFIG_ARCH_S3C2410)
  79.     printk("Now use the default MAC address: 10:23:45:67:89:ab ");
  80.     mac_src = "EmbedSky";
  81.     ndev->dev_addr[0] = 0x10;
  82.     ndev->dev_addr[1] = 0x23;
  83.     ndev->dev_addr[2] = 0x45;
  84.     ndev->dev_addr[3] = 0x67;
  85.     ndev->dev_addr[4] = 0x89;
  86.     ndev->dev_addr[5] = 0xab;
  87. #else
  88.     mac_src = "eeprom";

  89.     /* try reading the node address from the attached EEPROM */         //读取MAC地址,从EEPROM中
  90.     for (i = 0; i < 6; i += 2)
  91.         dm9000_read_eeprom(db, i / 2, ndev->dev_addr+i);

  92.     if (!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) {
  93.         mac_src = "platform data";
  94.         memcpy(ndev->dev_addr, pdata->dev_addr, 6);
  95.     }

  96.     if (!is_valid_ether_addr(ndev->dev_addr)) {
  97.         /* try reading from mac */
  98.         
  99.         mac_src = "chip";
  100.         for (i = 0; i < 6; i++)
  101.             ndev->dev_addr[i] = ior(db, i+DM9000_PAR);
  102.     }

  103.     if (!is_valid_ether_addr(ndev->dev_addr))
  104.         dev_warn(db->dev, "%s: Invalid ethernet MAC address. Please "
  105.              "set using ifconfig ", ndev->name);
  106. #endif

  107.     platform_set_drvdata(pdev, ndev);
  108.     ret = register_netdev(ndev);                                           //注册网卡驱动

  109.     if (ret == 0)
  110.         printk(KERN_INFO "%s: dm9000%c at %p,%p IRQ %d MAC: %pM (%s) ",
  111.          ndev->name, dm9000_type_to_char(db->type),
  112.          db->io_addr, db->io_data, ndev->irq,
  113.          ndev->dev_addr, mac_src);
  114.     return 0;

  115. out:
  116. #if defined(CONFIG_ARCH_S3C2410)
  117.     *(volatile unsigned int *)S3C2410_BWSCON = oldval_bwscon;
  118.     *(volatile unsigned int *)S3C2410_BANKCON4 = oldval_bankcon4;
  119. #endif
  120.     dev_err(db->dev, "not found (%d). ", ret);

  121.     dm9000_release_board(pdev, db);
  122.     free_netdev(ndev);

  123.     return ret;
  124. }
二、DM9000_OPEN函数分析
在probe中没有太多的dm9000的初始化代码,都写在了open中(这个open函数在ifconfig eth0 192.168.1.1时调用):
  1. /*
  2.  * Open the interface.
  3.  * The interface is opened whenever "ifconfig" actives it.
  4.  */
  5. static int
  6. dm9000_open(struct net_device *dev)
  7. {
  8.     board_info_t *db = netdev_priv(dev);
  9.     unsigned long irqflags = db->irq_res->flags & IRQF_TRIGGER_MASK;

  10.     if (netif_msg_ifup(db))
  11.         dev_dbg(db->dev, "enabling %s ", dev->name);

  12.     /* If there is no IRQ type specified, default to something that
  13.      * may work, and tell the user that this is a problem */

  14.     if (irqflags == IRQF_TRIGGER_NONE)
  15.         dev_warn(db->dev, "WARNING: no IRQ resource flags set. ");

  16.     irqflags |= IRQF_SHARED;

  17.     if (request_irq(dev->irq, &dm9000_interrupt, irqflags, dev->name, dev))                      //注册网卡驱动的中断处理函数
  18.         return -EAGAIN;

  19.     /* Initialize DM9000 board */
  20. //    dm9000_reset(db);
  21.     dm9000_init_dm9000(dev);                                                                     //网卡初始化在这里

  22.     /* Init driver variable */
  23.     db->dbug_cnt = 0;

  24.     mii_check_media(&db->mii, netif_msg_link(db), 1);
  25.     netif_start_queue(dev);                                                                      //启动发送队列
  26.     
  27.     dm9000_schedule_poll(db);

  28.     return 0;
  29. }
DM9000初始化:
①分配net_device结构
②从platform_device中获取地址,中断号
③把获取到的地址映射为虚拟地址
④读取芯片类型
⑤设置操作函数集
⑥注册网卡驱动

三、dm9000_start_xmit发送函数分析

dm9000_start_xmit:
  1. /*
  2.  * Hardware start transmission.
  3.  * Send a packet to media from the upper layer.
  4.  */
  5. static int
  6. dm9000_start_xmit(struct sk_buff *skb, struct net_device *dev)
  7. {
  8.     unsigned long flags;
  9.     board_info_t *db = netdev_priv(dev);
  10.     int save_mwr, check_mwr, calc_mwr;    

  11.     dm9000_dbg(db, 3, "%s: ", __func__);

  12.     if ((db->tx_pkt_cnt > 0) || !netif_carrier_ok(dev))
  13.         return 1;

  14.     spin_lock_irqsave(&db->lock, flags);
  15.     
  16.     netif_stop_queue(dev);                                                                                     //通知协议栈,暂停向驱动传送数据
  17.     db->tx_pkt_cnt++;
  18.     dev->stats.tx_bytes += skb->len;
  19.     dev->stats.tx_packets++;
  20.     
  21.     save_mwr = (ior(db, 0xfb) << 8) | ior(db, 0xfa);
  22.     calc_mwr = save_mwr + skb->len;
  23.     if(skb->len & 0x01) calc_mwr++;
  24.     if(calc_mwr > 0x0bff ) calc_mwr -= 0x0c00;

  25.     /* Set TX length to DM9000 */                                                                              //获取发送长度
  26.     iow(db, DM9000_TXPLL, skb->len);
  27.     iow(db, DM9000_TXPLH, skb->len >> 8);
  28.     
  29.     /* Move data to DM9000 TX RAM */                                                                           //将要发送的数据发送入缓存
  30.     writeb(DM9000_MWCMD, db->io_addr);
  31.     (db->outblk)(db->io_data, skb->data, skb->len);                                                            //skb->data,skb->len分别指向结构体数据的头和尾

  32.     /* Issue TX polling command */
  33.     iow(db, DM9000_TCR, TCR_TXREQ);    /* Cleared after TX complete */                                         //清除发送位

  34.     dev->trans_start = jiffies;    /* save the time stamp */
  35.     
  36.     check_mwr = (ior(db, 0xfb) << 8) | ior(db, 0xfa);
  37.     if(calc_mwr != check_mwr)
  38.     {
  39.         printk(KERN_INFO "TX: fifo error %04x %04x %04x %04x ", save_mwr, skb->len, calc_mwr, check_mwr);
  40.         iow(db, 0xfb, (calc_mwr >> 8) & 0xff);
  41.         iow(db, 0xfa, calc_mwr & 0xff);
  42.     }            

  43.     spin_unlock_irqrestore(&db->lock, flags);

  44.     /* free this SKB */
  45.     dev_kfree_skb(skb);                                                                                          //释放SKB结构

  46.     return 0;
  47. }
发送完成产生中断(dm9000_interrupt):
  1. static irqreturn_t dm9000_interrupt(int irq, void *dev_id)
  2. {
  3.     struct net_device *dev = dev_id;
  4.     board_info_t *db = netdev_priv(dev);
  5.     int int_status;
  6.     unsigned long flags;
  7.     u8 reg_save;

  8.     dm9000_dbg(db, 3, "entering %s ", __func__);

  9.     /* A real interrupt coming */

  10.     /* holders of db->lock must always block IRQs */
  11.     spin_lock_irqsave(&db->lock, flags);

  12.     /* Save previous register address */
  13.     reg_save = readb(db->io_addr);

  14.     /* Disable all interrupts */
  15.     iow(db, DM9000_IMR, IMR_PAR);

  16.     /* Got DM9000 interrupt status */
  17.     int_status = ior(db, DM9000_ISR);    /* Got ISR */
  18.     iow(db, DM9000_ISR, int_status);    /* Clear ISR status */

  19.     if (netif_msg_intr(db))
  20.         dev_dbg(db->dev, "interrupt status %02x ", int_status);

  21.     /* Received the coming packet */
  22.     if (int_status & ISR_PRS)
  23.         dm9000_rx(dev);                                                               //接收中断
  24.     
  25.     int_status |= ior(db, DM9000_ISR);    /* Got ISR */

  26.     /* Trnasmit Interrupt check */
  27.     if (int_status & ISR_PTS)
  28.     {
  29.         iow(db, DM9000_ISR, ISR_PTS);
  30.         dm9000_tx_done(dev, db);                                                      //发送中断
  31.     }

  32.     if (db->type != TYPE_DM9000E) {
  33.         if (int_status & ISR_LNKCHNG) {
  34.             /* fire a link-change request */
  35.             schedule_delayed_work(&db->phy_poll, 1);
  36.         }
  37.     }

  38.     /* Re-enable interrupt mask */
  39.     iow(db, DM9000_IMR, db->imr_all);

  40.     /* Restore previous register address */
  41.     writeb(reg_save, db->io_addr);

  42.     spin_unlock_irqrestore(&db->lock, flags);

  43.     return IRQ_HANDLED;
  44. }
发送函数完成后进入中断dm9000_tx_done:
  1. /*
  2.  * DM9000 interrupt handler
  3.  * receive the packet to upper layer, free the transmitted packet
  4.  */

  5. static void dm9000_tx_done(struct net_device *dev, board_info_t *db)
  6. {
  7.     int tx_status = ior(db, DM9000_TCR);    /* Got TX status */                           //获取中断状态
  8.     
  9.     if(tx_status & TCR_TXREQ)                                                             //查看发送是否出错    
  10.     {
  11.         dev->stats.rx_fifo_errors++;                                                      //错误计数加1
  12.     }
  13.     else
  14.     {
  15.         db->tx_pkt_cnt = 0;
  16.         dev->trans_start = 0;
  17.         netif_wake_queue(dev);                                                            //调用netif_wake_queue(),通知协议栈发送数据
  18.     }
  19. }
dm9000_rx():
  1. /*
  2.  * Received a packet and pass to upper layer
  3.  */
  4. static void
  5. dm9000_rx(struct net_device *dev)
  6. {
  7.     board_info_t *db = netdev_priv(dev);
  8.     struct dm9000_rxhdr rxhdr;
  9.     struct sk_buff *skb;
  10.     u8 rxbyte, *rdptr;
  11.     bool GoodPacket;
  12.     int RxLen;
  13.     int save_mrr, check_mrr, calc_mrr;

  14.     /* Check packet ready or not */
  15.     do {
  16.         ior(db, DM9000_MRCMDX);    /* Dummy read */                                                     //空读
  17.         save_mrr = (ior(db, 0xf5) << 8) | ior(db, 0xf4);
  18.         /* Get most updated data */
  19.         rxbyte = ior(db, DM9000_MRCMDX);                                                                //查看接收状态
  20.         
  21.         if(DM9000_PKT_RDY != rxbyte)                                                                    //是否已经ready
  22.         {
  23.             /* Status check: this byte must be 0 or 1 */
  24.             if (rxbyte) {
  25. //                dev_warn(db->dev, "status check fail: %d ", rxbyte);
  26.                 printk(KERN_INFO "status check fail: %d ", rxbyte);
  27.                 iow(db, DM9000_RCR, 0x00);    /* Stop Device */
  28.                 iow(db, DM9000_IMR, IMR_PAR);    /* Stop INT request */
  29.                 
  30.                 dm9000_init_dm9000(dev);
  31.             }
  32.             return;
  33.         }

  34.         /* A packet ready now & Get status/length */
  35.         GoodPacket = true;                                                           
  36.         writeb(DM9000_MRCMD, db->io_addr);                                                                //
  37.         (db->inblk)(db->io_data, &rxhdr, sizeof(rxhdr));                                                  //dm9000_rxhdr结构中有rx_len,接收长度

  38.         calc_mrr = save_mrr + 4;
  39.         if(calc_mrr > 0x3fff) calc_mrr -= 0x3400;
  40.         
  41.         check_mrr = (ior(db, 0xf5) << 8) | ior(db, 0xf4);
  42.         if(calc_mrr != check_mrr)
  43.         {
  44.             printk(KERN_INFO "RX: 4 byte error %04x %04x %04x ", save_mrr, calc_mrr, check_mrr);
  45.             iow(db, 0xf5, (save_mrr >> 8) & 0xff);
  46.             iow(db, 0xf4, save_mrr & 0xff);
  47.             continue;
  48.         }
  49.         
  50.         writeb(DM9000_MRCMD, db->io_addr);
  51.         
  52.         RxLen = le16_to_cpu(rxhdr.RxLen);
  53.         
  54.         calc_mrr = save_mrr + 4 + RxLen;
  55.         if(RxLen & 0x01) calc_mrr++;
  56.         if(calc_mrr > 0x3fff) calc_mrr -= 0x3400;

  57.         if (netif_msg_rx_status(db))
  58.             dev_dbg(db->dev, "RX: status %02x, length %04x ",
  59.                 rxhdr.RxStatus, RxLen);

  60.         /* Packet Status check */                                                                     //判断长度
  61.         if (RxLen < 0x40) {
  62.             GoodPacket = false;
  63.             if (netif_msg_rx_err(db))
  64.                 dev_dbg(db->dev, "RX: Bad Packet (runt) ");
  65.         }

  66.         if (RxLen > DM9000_PKT_MAX) {
  67.             dev_dbg(db->dev, "RST: RX Len:%x ", RxLen);
  68.         }

  69.         /* rxhdr.RxStatus is identical to RSR register. */                                             //读取状态看有没有出错
  70.         if (rxhdr.RxStatus & (RSR_FOE | RSR_CE | RSR_AE |
  71.                  RSR_PLE | RSR_RWTO |
  72.                  RSR_LCS | RSR_RF)) {
  73.             GoodPacket = false;
  74.             if (rxhdr.RxStatus & RSR_FOE) {
  75.                 if (netif_msg_rx_err(db))
  76.                     dev_dbg(db->dev, "fifo error ");
  77.                 dev->stats.rx_fifo_errors++;
  78.             }
  79.             if (rxhdr.RxStatus & RSR_CE) {
  80.                 if (netif_msg_rx_err(db))
  81.                     dev_dbg(db->dev, "crc error ");
  82.                 dev->stats.rx_crc_errors++;
  83.             }
  84.             if (rxhdr.RxStatus & RSR_RF) {
  85.                 if (netif_msg_rx_err(db))
  86.                     dev_dbg(db->dev, "length error ");
  87.                 dev->stats.rx_length_errors++;
  88.             }
  89.         }

  90.         /* Move data from DM9000 */                                                                       
  91.         if (GoodPacket
  92.          && ((skb = dev_alloc_skb(RxLen + 4)) != NULL)) {                                                    //分配skb=接收到的数据长度+4
  93.             skb_reserve(skb, 2);                                                                             //skb中的ip包需要4字节对齐,所以skb要保留多2个字节。(以太网包格式)
  94.             rdptr = (u8 *) skb_put(skb, RxLen - 4);                                                          //skb的tail指针,数据包长度减4

  95.             /* Read received packet from RX SRAM */

  96.             (db->inblk)(db->io_data, rdptr, RxLen);                                                          //读取io_data地址到rdptr中
  97.             dev->stats.rx_bytes += RxLen;

  98.             /* Pass to upper layer */
  99.             skb->protocol = eth_type_trans(skb, dev);
  100.             netif_rx(skb);                                                                                   //把准备好的skb提交给上层协议
  101.             dev->stats.rx_packets++;
  102.             
  103.             check_mrr = (ior(db, 0xf5) << 8) | ior(db, 0xf4);
  104.             if(calc_mrr != check_mrr)
  105.             {
  106. //                dev_dbg(db->dev, "RX: fifo error %04x %04x %04x %04x ", save_mrr, RxLen, calc_mrr, check_mrr);
  107.                 printk(KERN_INFO "RX: fifo error %04x %04x %04x %04x ", save_mrr, RxLen, calc_mrr, check_mrr);
  108.                 iow(db, 0xf5, (calc_mrr >> 8) & 0xff);
  109.                 iow(db, 0xf4, calc_mrr & 0xff);
  110.             }            

  111.         } else {
  112.             /* need to dump the packet's data */
  113.             iow(db, 0xf5, (calc_mrr >> 8) & 0xff);
  114.             iow(db, 0xf4, calc_mrr & 0xff);
  115.         }
  116.         
  117.     } while (rxbyte == DM9000_PKT_RDY);
  118. }









无欲速,无见小利。欲速,则不达;见小利,则大事不成。
原文地址:https://www.cnblogs.com/ch122633/p/7363302.html