mini2440 dm9000 网卡驱动详解 (三)

mini2440 dm9000 网卡驱动详解 (三)

*dm9000_get_drvinfo()

 

    该函数去的设备的基本信息(设备名,版本,总线名)传给ethtool_drvinfo结构体变量。代码清单如下:

 

 

[cpp]  view plain copy
  1. static void dm9000_get_drvinfo(struct net_device *dev,      
  2.                    struct ethtool_drvinfo *info)      
  3. {      
  4.     board_info_t *dm = to_dm9000_board(dev); /*to_dm9000_board实际上就是调用了netdev_priv(dev)*/     
  5.      
  6.     strcpy(info->driver, CARDNAME);      
  7.     strcpy(info->version, DRV_VERSION);      
  8.     strcpy(info->bus_info, to_platform_device(dm->dev)->name);      
  9. }    

   *dm9000_get_settings()

 

    该函数得到由参数cmd指定的设置信息。

 

   *dm9000_set_settings()

 

    该函数设置由参数cmd指定的信息。

 

   *dm9000_get_msglevel()

 

   *dm9000_set_msglevel()

 

    这两个函数设置和取得messagelevel,实际是设置和取得board_info中的msg_enable信息。

 

   *dm9000_nway_reset()

 

    重启mii的自动协商

 

   *dm9000_get_link()

 

    该函数的到link状态。如果带外部PHY,则返回mii链接状态。 否则返回DM9000 NSR寄存器数值。

 

   *dm9000_get_eeprom_len()

     dm9000_get_eeprom()

     dm9000_set_eeprom()

 

    这三个函数用来读写eeprom。

5. 与数据传输有关的函数。

 

     上面已经分析了一个与数据传输有关的函数,那就是发送数据的函数dm9000_start_xmit()。这里再来分析数据的接收。再看具体代码之前还是来看看DM9000的数据接收的过程。

 

 

     接收的数据存储在RX SRAM中,地址是0C00h~3FFFh。存储在RX_SRAM中的每个包都有4个字节的信息头。可以使用MRCMDX和MRCMD寄存器来得到这些信息。第一个字节用来检查数据包是否接收到了RX_SRAM中,如果这个字节是"01",意味着一个包已经接收。如果是"00",则还没有数据包被接收到RX_SRAM中。第二个字节保存接收到的数据包的信息,格式和RSR寄存器一样。根据这个格式,接收到的包能被校验是正确的还是错误的包。第三和第四字节保存了接收的数据包的长度。这四个字节以外的其他字节就是接收包的数据。看下图可以更好的理解这种格式。

根据包的结构可以知道接收一个包应该按照下面的步骤来进行:

 

第一步:判断包是否已经接收过来了。需要用到MRCMDX寄存器。MRCMDX寄存器是存储数据读命令寄存器(地址不增加)。这个寄存器只是用来读接收包标志位"01"。下面这段代码是一个例子,用来判断RX ready:

[cpp]  view plain copy
  1. u8 RX_ready = ior( IOaddr, 0xF0 );         /* dummy read the packet ready flag */     
  2. RX_ready = (u8) inp( IOaddr + 4 );         /* got the most updated data */     
  3. if ( RX_ready == 1 ) {                     /* ready check: this byte must be 0 or 1 */     
  4.        /* check the RX status and to get RX length (see datasheet ch.5.6.3) */     
  5.        /* income RX a packet (see datasheet ch.5.6.4) */     
  6. else if ( RX_ready != 0 ) {              /* stop device and wait to reset device */     
  7.        iow( IOaddr, 0xFF, 0x80 );          /* stop INT request */     
  8.        iow( IOaddr, 0xFE, 0x0F );          /* clear ISR status */     
  9.        iow( IOaddr, 0x05, 0x00 );          /* stop RX function */     
  10.        u8 device_wait_reset = TRUE;        /* raise the reset flag */     
  11. }    

 第二步:检查包的状态和长度。需要用到MRCMD寄存器(存储数据读命令,读指针自动增加)。下面这段例子代码用来读RX状态和长度。

 

[cpp]  view plain copy
  1. u8 io_mode = ior( IOaddr, 0xFE ) >> 6; /* ISR bit[7:6] keep I/O mode */     
  2. outp( IOaddr, 0xF2 );                /* trigger MRCMD reg_F2h with read_ptr++ */     
  3. /* int RX_status : the RX packet status, int RX_length : the RX packet length */     
  4. if ( io_mode == 2 ) {                /* I/O byte mode */     
  5. RX_status = inp( IOaddr + 4 ) + ( inp( IOaddr + 4 ) << 8 );      
  6. RX_length = inp( IOaddr + 4 ) + ( inp( IOaddr + 4 ) << 8 );        }      
  7. else if ( io_mode == 0 ) {           /* I/O word mode */     
  8. RX_status = inpw( IOaddr + 4 );      
  9. RX_length = inpw( IOaddr + 4 );             }      
  10. else if ( io_mode == 1 ) {           /* I/O dword mode */     
  11. (u32) status_tmp = inpl( IOaddr + 4 );           /* got the RX 32-bit dword data */     
  12. RX_status = (u16)( status_tmp & 0xFFFF );      
  13. RX_length = (u16)( ( status_tmp >> 16 ) & 0xFFFF );          }    

第三步:读包的数据。也需要MRCMD寄存器。例子代码如下:

[cpp]  view plain copy
  1. /* u8 RX_data[] : the data of the received packet */     
  2. if ( io_mode == 2 ) {                 /* I/O byte mode */     
  3. for ( i = 0 ; i < RX_length ; i++ ) /* loop to read a byte data from RX SRAM */     
  4.   RX_data[ i ] = (u8) inp( IOaddr + 4 );          }      
  5. else if ( io_mode == 0 ) {            /* I/O word mode */     
  6. int length_tmp = ( RX_length + 1 ) / 2;      
  7. for ( i = 0 ; i < length_tmp ; i++ ) /* loop to read a word data from RX SRAM */     
  8.  ( (u16 *)RX_data)[ i ] = inpw( IOaddr + 4 );           }      
  9. else if ( io_mode == 1 ) {            /* I/O dword mode */     
  10. int length_tmp = ( RX_length + 3 ) / 4;      
  11. for ( i = 0 ; i < length_tmp ; i++ ) /* loop to read a dword data from RX SRAM */     
  12.  ( (u32 *)RX_data)[ i ] = inpl( IOaddr + 4 ); }         /* inpl() is inport 32-bit I/O */    

下面的dm9000_rx()函数实际上是按照上面这三个步骤来实现的,具体实现并不一定是要参照例子代码。注意这里按照DM9000接收包的格式定义了一个结构体dm9000_rxhdr用来表示头部的四个字节。代码清单如下:

 

[cpp]  view plain copy
  1. struct dm9000_rxhdr {      
  2.     u8  RxPktReady;      
  3.     u8  RxStatus;      
  4.     __le16  RxLen;      
  5. } __attribute__((__packed__));    

接收函数代码如下:

 

[cpp]  view plain copy
  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.      
  14.     /* Check packet ready or not */     
  15.     do {      
  16.         ior(db, DM9000_MRCMDX); /* Dummy read */     
  17.      
  18.         /* Get most updated data */     
  19.                 /*读一下最新数据的第一个字节*/     
  20.         rxbyte = readb(db->io_data);      
  21.      
  22.         /* Status check: this byte must be 0 or 1 */     
  23.                 /*DM9000_PKT_RDY定义是0x01,如果第一个字节大于0x01,则不是正确的状态。因为第一个字节只能是01h或00h*/     
  24.         if (rxbyte > DM9000_PKT_RDY) {      
  25.             dev_warn(db->dev, "status check fail: %d\n", rxbyte);      
  26.             iow(db, DM9000_RCR, 0x00);  /* Stop Device */     
  27.             iow(db, DM9000_ISR, IMR_PAR);   /* Stop INT request */     
  28.             return;      
  29.         }      
  30.      
  31.         if (rxbyte != DM9000_PKT_RDY)      
  32.             return;      
  33.      
  34.         /* A packet ready now  & Get status/length */     
  35.         GoodPacket = true;      
  36.         writeb(DM9000_MRCMD, db->io_addr);      
  37.      
  38.         (db->inblk)(db->io_data, &rxhdr, sizeof(rxhdr));/*一次性读入四个字节的内容到rxhdr变量*/     
  39.      
  40.         RxLen = le16_to_cpu(rxhdr.RxLen);      
  41.      
  42.         if (netif_msg_rx_status(db))      
  43.             dev_dbg(db->dev, "RX: status %02x, length %04x\n",      
  44.                 rxhdr.RxStatus, RxLen);      
  45.      
  46.         /* Packet Status check */     
  47.         if (RxLen < 0x40) {      
  48.             GoodPacket = false;      
  49.             if (netif_msg_rx_err(db))      
  50.                 dev_dbg(db->dev, "RX: Bad Packet (runt)\n");      
  51.         }      
  52.      
  53.         if (RxLen > DM9000_PKT_MAX) {      
  54.             dev_dbg(db->dev, "RST: RX Len:%x\n", RxLen);      
  55.         }      
  56.      
  57.         /* rxhdr.RxStatus is identical to RSR register. */     
  58.         if (rxhdr.RxStatus & (RSR_FOE | RSR_CE | RSR_AE |      
  59.                       RSR_PLE | RSR_RWTO |      
  60.                       RSR_LCS | RSR_RF)) {      
  61.             GoodPacket = false;      
  62.             if (rxhdr.RxStatus & RSR_FOE) {      
  63.                 if (netif_msg_rx_err(db))      
  64.                     dev_dbg(db->dev, "fifo error\n");      
  65.                 dev->stats.rx_fifo_errors++;      
  66.             }      
  67.             if (rxhdr.RxStatus & RSR_CE) {      
  68.                 if (netif_msg_rx_err(db))      
  69.                     dev_dbg(db->dev, "crc error\n");      
  70.                 dev->stats.rx_crc_errors++;      
  71.             }      
  72.             if (rxhdr.RxStatus & RSR_RF) {      
  73.                 if (netif_msg_rx_err(db))      
  74.                     dev_dbg(db->dev, "length error\n");      
  75.                 dev->stats.rx_length_errors++;      
  76.             }      
  77.         }      
  78.      
  79.         /* Move data from DM9000 */     
  80.                 /*关键的代码就是这里啦。使用到了上面提到的sk_buff。将RX SRAM中的data段数据放入sk_buff,然后发送给上层,至于怎么发送,不用去驱动操心了。sk_buff的protocol全部搞定*/     
  81.         if (GoodPacket      
  82.             && ((skb = dev_alloc_skb(RxLen + 4)) != NULL)) {      
  83.             skb_reserve(skb, 2);      
  84.             rdptr = (u8 *) skb_put(skb, RxLen - 4);      
  85.      
  86.             /* Read received packet from RX SRAM */     
  87.      
  88.             (db->inblk)(db->io_data, rdptr, RxLen);      
  89.             dev->stats.rx_bytes += RxLen;      
  90.      
  91.             /* Pass to upper layer */     
  92.             skb->protocol = eth_type_trans(skb, dev);      
  93.             netif_rx(skb);      
  94.             dev->stats.rx_packets++;      
  95.      
  96.         } else {      
  97.             /* need to dump the packet's data */     
  98.      
  99.             (db->dumpblk)(db->io_data, RxLen);      
  100.         }      
  101.     } while (rxbyte == DM9000_PKT_RDY);      
  102. }    

6. 中断处理相关函数

 

  DM9000的驱动程序采用了中断方式而非轮询方式。触发中断的时机发生在:1)DM9000接收到一个包以后。2)DM9000发送完了一个包以后。

 

   中断处理函数在open的时候被注册进内核。代码清单如下:

 

[cpp]  view plain copy
  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.      
  9.     dm9000_dbg(db, 3, "entering %s\n", __func__);      
  10.      
  11.     /* A real interrupt coming */     
  12.      
  13.     /* holders of db->lock must always block IRQs */     
  14.     spin_lock_irqsave(&db->lock, flags);      
  15.      
  16.     /* Save previous register address */     
  17.     reg_save = readb(db->io_addr);      
  18.      
  19.     /* Disable all interrupts */     
  20.     iow(db, DM9000_IMR, IMR_PAR);      
  21.      
  22.     /* Got DM9000 interrupt status */     
  23.     int_status = ior(db, DM9000_ISR);   /* Got ISR */     
  24.     iow(db, DM9000_ISR, int_status);    /* Clear ISR status */     
  25.      
  26.     if (netif_msg_intr(db))      
  27.         dev_dbg(db->dev, "interrupt status %02x\n", int_status);      
  28.      
  29.     /* Received the coming packet */     
  30.         /*如果是由于收到数据而触发的中断,显然调用dm9000_rx()把数据取走,传递给上层*/     
  31.     if (int_status & ISR_PRS)      
  32.         dm9000_rx(dev);      
  33.      
  34.     /* Trnasmit Interrupt check */     
  35.         /*如果是由于发送完了数据而触发的中断,则调用dm9000_tx_done()函数,下面具体分析这个函数*/     
  36.     if (int_status & ISR_PTS)      
  37.         dm9000_tx_done(dev, db);      
  38.      
  39.     if (db->type != TYPE_DM9000E) {      
  40.         if (int_status & ISR_LNKCHNG) {      
  41.             /* fire a link-change request */     
  42.             schedule_delayed_work(&db->phy_poll, 1);      
  43.         }      
  44.     }      
  45.      
  46.     /* Re-enable interrupt mask */     
  47.     iow(db, DM9000_IMR, db->imr_all);      
  48.      
  49.     /* Restore previous register address */     
  50.     writeb(reg_save, db->io_addr);      
  51.      
  52.     spin_unlock_irqrestore(&db->lock, flags);      
  53.      
  54.     return IRQ_HANDLED;      
  55. }    

*dm9000_tx_done()

 

   注:dm9000可以发送两个数据包,当发送一个数据包产生中断后,要确认一下队列中有没有第2个包需要发送。

 

 

 

    (1)读取dm9000寄存器NSR(NetworkStatus Register)获取发送的状态,存在变量tx_status中;

 

    (2)如果发送状态为NSR_TX2END(第2个包发送完毕)或者NSR_TX1END(第1个包发送完毕),则将待发送的数据包数量(db-> tx_pkt_cnt )减1,已发送的数据包数量(dev->stats.tx_packets)加1;

 

    (3)检查变量db->tx_pkt_cnt(待发送的数据包)是否大于0(表明还有数据包要发送),则调用函数dm9000_send_packet发送队列中的数据包;

 

    (4)调用函数netif_wake_queue(dev)通知内核可以将待发送的数据包进入发送队列。

 

[cpp]  view plain copy
  1. /*    
  2.  * DM9000 interrupt handler    
  3.  * receive the packet to upper layer, free the transmitted packet    
  4.  */     
  5.      
  6. static void dm9000_tx_done(struct net_device *dev, board_info_t *db)      
  7. {      
  8.     int tx_status = ior(db, DM9000_NSR);    /* Got TX status */     
  9.      
  10.     if (tx_status & (NSR_TX2END | NSR_TX1END)) {      
  11.         /* One packet sent complete */     
  12.         db->tx_pkt_cnt--;      
  13.         dev->stats.tx_packets++;      
  14.      
  15.         if (netif_msg_tx_done(db))      
  16.             dev_dbg(db->dev, "tx done, NSR %02x\n", tx_status);      
  17.      
  18.         /* Queue packet check & send */     
  19.         if (db->tx_pkt_cnt > 0) {      
  20.             iow(db, DM9000_TXPLL, db->queue_pkt_len);      
  21.             iow(db, DM9000_TXPLH, db->queue_pkt_len >> 8);      
  22.             iow(db, DM9000_TCR, TCR_TXREQ);      
  23.             dev->trans_start = jiffies;      
  24.         }      
  25.         netif_wake_queue(dev);      
  26.     }      
  27. }    

   7.一些操作硬件细节的函数。

 

   在看函数之前还是先来看一下DM9000CMD Pin 和Processor并行总线的连接关系。CMD管脚用来设置命令类型。当CMD管脚拉高时,这个命令周期访问DATA_PORT。如果拉低, 则这个命令周期访问ADDR_PORT。见下图:

当然,内存映射的I/O空间读写还是采用最基本的readb(), readw(), readl(), writeb(), writew(), writel() , readsb(),readsw(), readsl(), writesb(), writesw(), writesl() 。

 

在DM9000的驱动中还自定义了几个函数,方便操作。

 

    * ior()

 

       从IO端口读一个字节。代码清单如下:

[cpp]  view plain copy
  1. static u8 ior(board_info_t * db, int reg)      
  2. {      
  3.     writeb(reg, db->io_addr); /*写reg到ADDR_PORT,用来选择寄存器*/     
  4.     return readb(db->io_data); /*从DATA_PORT读一个字节,用来读寄存器*/     
  5. }    
  6.   
  7.     * iow()  
  8.   
  9.         向IO端口写一个字节。代码清单如下:  
  10.   
  11. /*    
  12.  *   Write a byte to I/O port    
  13.  */     
  14.      
  15. static void     
  16. iow(board_info_t * db, int reg, int value)      
  17. {      
  18.     writeb(reg, db->io_addr);      
  19.     writeb(value, db->io_data);      
  20. }    

此外还有dm9000_outblk_8bit(), dm9000_outblk_16bit(), dm9000_outblk_32bit(),dm9000_inblk_8bit(), dm9000_inblk_16bit(), dm9000_inblk_32bit()等等。不一一解释。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值