*dm9000_get_drvinfo()
该函数去的设备的基本信息(设备名,版本,总线名)传给ethtool_drvinfo结构体变量。代码清单如下:
static void dm9000_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { board_info_t *dm = to_dm9000_board(dev); /*to_dm9000_board实际上就是调用了netdev_priv(dev)*/ strcpy(info->driver, CARDNAME); strcpy(info->version, DRV_VERSION); strcpy(info->bus_info, to_platform_device(dm->dev)->name); }
*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:
u8 RX_ready = ior( IOaddr, 0xF0 ); /* dummy read the packet ready flag */ RX_ready = (u8) inp( IOaddr + 4 ); /* got the most updated data */ if ( RX_ready == 1 ) { /* ready check: this byte must be 0 or 1 */ /* check the RX status and to get RX length (see datasheet ch.5.6.3) */ /* income RX a packet (see datasheet ch.5.6.4) */ } else if ( RX_ready != 0 ) { /* stop device and wait to reset device */ iow( IOaddr, 0xFF, 0x80 ); /* stop INT request */ iow( IOaddr, 0xFE, 0x0F ); /* clear ISR status */ iow( IOaddr, 0x05, 0x00 ); /* stop RX function */ u8 device_wait_reset = TRUE; /* raise the reset flag */ }
第二步:检查包的状态和长度。需要用到MRCMD寄存器(存储数据读命令,读指针自动增加)。下面这段例子代码用来读RX状态和长度。
u8 io_mode = ior( IOaddr, 0xFE ) >> 6; /* ISR bit[7:6] keep I/O mode */ outp( IOaddr, 0xF2 ); /* trigger MRCMD reg_F2h with read_ptr++ */ /* int RX_status : the RX packet status, int RX_length : the RX packet length */ if ( io_mode == 2 ) { /* I/O byte mode */ RX_status = inp( IOaddr + 4 ) + ( inp( IOaddr + 4 ) << 8 ); RX_length = inp( IOaddr + 4 ) + ( inp( IOaddr + 4 ) << 8 ); } else if ( io_mode == 0 ) { /* I/O word mode */ RX_status = inpw( IOaddr + 4 ); RX_length = inpw( IOaddr + 4 ); } else if ( io_mode == 1 ) { /* I/O dword mode */ (u32) status_tmp = inpl( IOaddr + 4 ); /* got the RX 32-bit dword data */ RX_status = (u16)( status_tmp & 0xFFFF ); RX_length = (u16)( ( status_tmp >> 16 ) & 0xFFFF ); }
第三步:读包的数据。也需要MRCMD寄存器。例子代码如下:
/* u8 RX_data[] : the data of the received packet */ if ( io_mode == 2 ) { /* I/O byte mode */ for ( i = 0 ; i < RX_length ; i++ ) /* loop to read a byte data from RX SRAM */ RX_data[ i ] = (u8) inp( IOaddr + 4 ); } else if ( io_mode == 0 ) { /* I/O word mode */ int length_tmp = ( RX_length + 1 ) / 2; for ( i = 0 ; i < length_tmp ; i++ ) /* loop to read a word data from RX SRAM */ ( (u16 *)RX_data)[ i ] = inpw( IOaddr + 4 ); } else if ( io_mode == 1 ) { /* I/O dword mode */ int length_tmp = ( RX_length + 3 ) / 4; for ( i = 0 ; i < length_tmp ; i++ ) /* loop to read a dword data from RX SRAM */ ( (u32 *)RX_data)[ i ] = inpl( IOaddr + 4 ); } /* inpl() is inport 32-bit I/O */
下面的dm9000_rx()函数实际上是按照上面这三个步骤来实现的,具体实现并不一定是要参照例子代码。注意这里按照DM9000接收包的格式定义了一个结构体dm9000_rxhdr用来表示头部的四个字节。代码清单如下:
struct dm9000_rxhdr { u8 RxPktReady; u8 RxStatus; __le16 RxLen; } __attribute__((__packed__));
接收函数代码如下:
/* * Received a packet and pass to upper layer */ static void dm9000_rx(struct net_device *dev) { board_info_t *db = netdev_priv(dev); struct dm9000_rxhdr rxhdr; struct sk_buff *skb; u8 rxbyte, *rdptr; bool GoodPacket; int RxLen; /* Check packet ready or not */ do { ior(db, DM9000_MRCMDX); /* Dummy read */ /* Get most updated data */ /*读一下最新数据的第一个字节*/ rxbyte = readb(db->io_data); /* Status check: this byte must be 0 or 1 */ /*DM9000_PKT_RDY定义是0x01,如果第一个字节大于0x01,则不是正确的状态。因为第一个字节只能是01h或00h*/ if (rxbyte > DM9000_PKT_RDY) { dev_warn(db->dev, "status check fail: %d\n", rxbyte); iow(db, DM9000_RCR, 0x00); /* Stop Device */ iow(db, DM9000_ISR, IMR_PAR); /* Stop INT request */ return; } if (rxbyte != DM9000_PKT_RDY) return; /* A packet ready now & Get status/length */ GoodPacket = true; writeb(DM9000_MRCMD, db->io_addr); (db->inblk)(db->io_data, &rxhdr, sizeof(rxhdr));/*一次性读入四个字节的内容到rxhdr变量*/ RxLen = le16_to_cpu(rxhdr.RxLen); if (netif_msg_rx_status(db)) dev_dbg(db->dev, "RX: status %02x, length %04x\n", rxhdr.RxStatus, RxLen); /* Packet Status check */ if (RxLen < 0x40) { GoodPacket = false; if (netif_msg_rx_err(db)) dev_dbg(db->dev, "RX: Bad Packet (runt)\n"); } if (RxLen > DM9000_PKT_MAX) { dev_dbg(db->dev, "RST: RX Len:%x\n", RxLen); } /* rxhdr.RxStatus is identical to RSR register. */ if (rxhdr.RxStatus & (RSR_FOE | RSR_CE | RSR_AE | RSR_PLE | RSR_RWTO | RSR_LCS | RSR_RF)) { GoodPacket = false; if (rxhdr.RxStatus & RSR_FOE) { if (netif_msg_rx_err(db)) dev_dbg(db->dev, "fifo error\n"); dev->stats.rx_fifo_errors++; } if (rxhdr.RxStatus & RSR_CE) { if (netif_msg_rx_err(db)) dev_dbg(db->dev, "crc error\n"); dev->stats.rx_crc_errors++; } if (rxhdr.RxStatus & RSR_RF) { if (netif_msg_rx_err(db)) dev_dbg(db->dev, "length error\n"); dev->stats.rx_length_errors++; } } /* Move data from DM9000 */ /*关键的代码就是这里啦。使用到了上面提到的sk_buff。将RX SRAM中的data段数据放入sk_buff,然后发送给上层,至于怎么发送,不用去驱动操心了。sk_buff的protocol全部搞定*/ if (GoodPacket && ((skb = dev_alloc_skb(RxLen + 4)) != NULL)) { skb_reserve(skb, 2); rdptr = (u8 *) skb_put(skb, RxLen - 4); /* Read received packet from RX SRAM */ (db->inblk)(db->io_data, rdptr, RxLen); dev->stats.rx_bytes += RxLen; /* Pass to upper layer */ skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); dev->stats.rx_packets++; } else { /* need to dump the packet's data */ (db->dumpblk)(db->io_data, RxLen); } } while (rxbyte == DM9000_PKT_RDY); }
6. 中断处理相关函数
DM9000的驱动程序采用了中断方式而非轮询方式。触发中断的时机发生在:1)DM9000接收到一个包以后。2)DM9000发送完了一个包以后。
中断处理函数在open的时候被注册进内核。代码清单如下:
static irqreturn_t dm9000_interrupt(int irq, void *dev_id) { struct net_device *dev = dev_id; board_info_t *db = netdev_priv(dev); int int_status; unsigned long flags; u8 reg_save; dm9000_dbg(db, 3, "entering %s\n", __func__); /* A real interrupt coming */ /* holders of db->lock must always block IRQs */ spin_lock_irqsave(&db->lock, flags); /* Save previous register address */ reg_save = readb(db->io_addr); /* Disable all interrupts */ iow(db, DM9000_IMR, IMR_PAR); /* Got DM9000 interrupt status */ int_status = ior(db, DM9000_ISR); /* Got ISR */ iow(db, DM9000_ISR, int_status); /* Clear ISR status */ if (netif_msg_intr(db)) dev_dbg(db->dev, "interrupt status %02x\n", int_status); /* Received the coming packet */ /*如果是由于收到数据而触发的中断,显然调用dm9000_rx()把数据取走,传递给上层*/ if (int_status & ISR_PRS) dm9000_rx(dev); /* Trnasmit Interrupt check */ /*如果是由于发送完了数据而触发的中断,则调用dm9000_tx_done()函数,下面具体分析这个函数*/ if (int_status & ISR_PTS) dm9000_tx_done(dev, db); if (db->type != TYPE_DM9000E) { if (int_status & ISR_LNKCHNG) { /* fire a link-change request */ schedule_delayed_work(&db->phy_poll, 1); } } /* Re-enable interrupt mask */ iow(db, DM9000_IMR, db->imr_all); /* Restore previous register address */ writeb(reg_save, db->io_addr); spin_unlock_irqrestore(&db->lock, flags); return IRQ_HANDLED; }
*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)通知内核可以将待发送的数据包进入发送队列。
/* * DM9000 interrupt handler * receive the packet to upper layer, free the transmitted packet */ static void dm9000_tx_done(struct net_device *dev, board_info_t *db) { int tx_status = ior(db, DM9000_NSR); /* Got TX status */ if (tx_status & (NSR_TX2END | NSR_TX1END)) { /* One packet sent complete */ db->tx_pkt_cnt--; dev->stats.tx_packets++; if (netif_msg_tx_done(db)) dev_dbg(db->dev, "tx done, NSR %02x\n", tx_status); /* Queue packet check & send */ if (db->tx_pkt_cnt > 0) { iow(db, DM9000_TXPLL, db->queue_pkt_len); iow(db, DM9000_TXPLH, db->queue_pkt_len >> 8); iow(db, DM9000_TCR, TCR_TXREQ); dev->trans_start = jiffies; } netif_wake_queue(dev); } }
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端口读一个字节。代码清单如下:
static u8 ior(board_info_t * db, int reg) { writeb(reg, db->io_addr); /*写reg到ADDR_PORT,用来选择寄存器*/ return readb(db->io_data); /*从DATA_PORT读一个字节,用来读寄存器*/ } * iow() 向IO端口写一个字节。代码清单如下: /* * Write a byte to I/O port */ static void iow(board_info_t * db, int reg, int value) { writeb(reg, db->io_addr); writeb(value, db->io_data); }
此外还有dm9000_outblk_8bit(), dm9000_outblk_16bit(), dm9000_outblk_32bit(),dm9000_inblk_8bit(), dm9000_inblk_16bit(), dm9000_inblk_32bit()等等。不一一解释。