现在的位置: 首页 > 综合 > 正文

DM9000网卡驱动分析

2014年03月06日 ⁄ 综合 ⁄ 共 21962字 ⁄ 字号 评论关闭

DM9000网卡驱动分析

#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/crc32.h>
#include <linux/mii.h>
#include <linux/ethtool.h>
#include <linux/dm9000.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/irq.h>

#include <asm/delay.h>
#include <asm/irq.h>
#include <asm/io.h>

#if defined(CONFIG_ARCH_S3C2410)
#include <mach/regs-mem.h>
#endif

#include "dm9000.h"

/* Board/System/Debug information/definition ---------------- */
//
EEPROMPHY地址寄存器中要选择内部PHY,那么7-6位强制为01
#define DM9000_PHY  0x40 /* PHY address 0x01 */

#define CARDNAME "dm9000"
#define DRV_VERSION "1.31"

/*
 * Transmit timeout, default 5 seconds.
 */
 //
传输超时时间设定,当传输超时时调用函数dm9000_timeout(structnet_device *dev)
static int watchdog = 5000;
module_param(watchdog, int, 0400);//
在驱动程序加载时可以重新设定watchdog
MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds");

/* DM9000 register address locking.
 *
 * The DM9000 uses an address register to control where data written
 * to the data register goes. This means that the address register
 * must be preserved over interrupts or similar calls.
 *
 * During interrupt and other critical calls, a spinlock is used to
 * protect the system, but the calls themselves save the address
 * in the address register in case they are interrupting another
 * access to the device.
 *
 * For general accesses a lock is provided so that calls which are
 * allowed to sleep are serialised so that the address register does
 * not need to be saved. This lock also serves to serialise access
 * to the EEPROM and PHY access registers which are shared between
 * these two devices.
 */

/* The driver supports the original DM9000E, and now the twonewer
 * devices, DM9000Aand DM9000B.
 */

enum dm9000_type {
 TYPE_DM9000E, /* original DM9000 */
 TYPE_DM9000A,
 TYPE_DM9000B
};

/* Structure/enum declaration ------------------------------- */
typedef struct board_info {
//cmd
脚决定了数据数据口还是地址索引
 void __iomem *io_addr; /* Register I/O base address */
 void __iomem *io_data; /* Data I/O address */
 u16   irq;  /* IRQ */

 u16  tx_pkt_cnt;//当前待传输的数据包的数量,最多两个
 //
第二个数据包长度存于此处,第二个数据包写入网卡SRAM中后要释放skb
 u16  queue_pkt_len;
 u16  queue_start_addr;
 u16  dbug_cnt;
 u8  io_mode;  /* 0:word, 2:byte */
 u8  phy_addr;
 u8  imr_all;
//
probe()函数中有db->flags= pdata->flags;
 unsigned int flags;
 unsigned int in_suspend :1;
 int  debug_level;

 enum dm9000_type type;

 void (*inblk)(void __iomem *port, void *data, int length);
 void (*outblk)(void __iomem *port, void *data, int length);
 void (*dumpblk)(void __iomem *port, int length);

 struct device *dev;      /*parent device */

 struct resource *addr_res;   /* resourcesfound */
 struct resource *data_res;//
物理地址
 struct resource *addr_req;   /* resources requested */
 struct resource *data_req;//I/O
映射后的虚拟地址
 struct resource *irq_res;

 struct mutex  addr_lock; /* phy and eepromaccess lock */
//
probe函数中初始化,处理函数dm9000_poll_work,链路连接状态改变
 struct delayed_work phy_poll;
 struct net_device  *ndev;

 spinlock_t lock;

 struct mii_if_info mii;
 u32  msg_enable;//
网络入口信息
} board_info_t;

/* debug code */

#define dm9000_dbg(db, lev, msg...) do {  \
 if ((lev) < CONFIG_DM9000_DEBUGLEVEL &&  \
     (lev) < db->debug_level) {   \
  dev_dbg(db->dev, msg);   \
 }      \
} while (0)

static inline board_info_t *to_dm9000_board(struct net_device*dev)
{//
存取私有数据指针专用函数
 return  netdev_priv(dev);
}

/* DM9000 network board routine ---------------------------- */

static void
dm9000_reset(board_info_t * db)
{
 dev_dbg(db->dev, "resetting device\n");
//NCR
00H):网络控制寄存器
 /* RESET device */
 writeb(DM9000_NCR, db->io_addr);
 udelay(200);
 writeb(NCR_RST, db->io_data);
 udelay(200);
}

/*
 *   Read a byte from I/O port
 */
static u8
ior(board_info_t * db, int reg)
{//
对网卡寄存器进行操作要先写该寄存器的偏移地址
 writeb(reg, db->io_addr);
 return readb(db->io_data);
}

/*
 *   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);
}

/* routines for sending block to chip */

static void dm9000_outblk_8bit(void __iomem *reg, void *data,int count)
{
 writesb(reg, data, count);
}

static void dm9000_outblk_16bit(void __iomem *reg, void *data,int count)
{
 writesw(reg, data, (count+1) >> 1);
}

static void dm9000_outblk_32bit(void __iomem *reg, void *data,int count)
{
 writesl(reg, data, (count+3) >> 2);
}

/* input block from chip to memory */

static void dm9000_inblk_8bit(void __iomem *reg, void *data, intcount)
{
 readsb(reg, data, count);
}


static void dm9000_inblk_16bit(void __iomem *reg, void *data, int count)
{
 readsw(reg, data, (count+1) >> 1);
}

static void dm9000_inblk_32bit(void __iomem *reg, void *data,int count)
{
 readsl(reg, data, (count+3) >> 2);
}

/* dump block from chip to null */
//
读出没用的数据,为了改变网卡SRAMFIFO的数据指针,
static void dm9000_dumpblk_8bit(void __iomem *reg, int count)
{
 int i;
 int tmp;

 for (i = 0; i < count; i++)
  tmp = readb(reg);
}

static void dm9000_dumpblk_16bit(void __iomem *reg, int count)
{
 int i;
 int tmp;

 count = (count + 1) >> 1;

 for (i = 0; i < count; i++)
  tmp = readw(reg);
}

static void dm9000_dumpblk_32bit(void __iomem *reg, int count)
{
 int i;
 int tmp;

 count = (count + 3) >> 2;

 for (i = 0; i < count; i++)
  tmp = readl(reg);
}

/* dm9000_set_io
 *
 * select the specified set of io routines to use with the
 * device
 */
//
设定I/O线宽
static void dm9000_set_io(struct board_info *db, int byte_width)
{
 /* use the size of the data resource to work out what IO
  * routines we want to use
  */

 switch (byte_width) {
 case 1:
  db->dumpblk = dm9000_dumpblk_8bit;
  db->outblk  = dm9000_outblk_8bit;
  db->inblk   = dm9000_inblk_8bit;
  break;


 case 3:
  dev_dbg(db->dev, ": 3 byte IO, falling back to16bit\n");
 case 2:
  db->dumpblk = dm9000_dumpblk_16bit;
  db->outblk  = dm9000_outblk_16bit;
  db->inblk   = dm9000_inblk_16bit;
  break;

 case 4:
 default:
  db->dumpblk = dm9000_dumpblk_32bit;
  db->outblk  = dm9000_outblk_32bit;
  db->inblk   = dm9000_inblk_32bit;
  break;
 }
}
/*
延时调度。用于检测网卡连接状态。
open函数中调用。
TYPE_DM9000E
在链路状态改变时是不产生中断的。
*/
static void dm9000_schedule_poll(board_info_t *db)
{
 if (db->type == TYPE_DM9000E)
  schedule_delayed_work(&db->phy_poll, HZ * 2);
}

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);
}

static unsigned int
dm9000_read_locked(board_info_t *db, int reg)
{
 unsigned long flags;
 unsigned int ret;

 spin_lock_irqsave(&db->lock,flags);
 ret = ior(db, reg);
 spin_unlock_irqrestore(&db->lock, flags);

 returnret;
}

static int dm9000_wait_eeprom(board_info_t *db)
{
 unsigned int status;
 int timeout = 8; /* wait max 8msec */

 /* The DM9000 data sheets say we should be able to
  * poll the ERRE bit in EPCR to wait for the EEPROM
  * operation. From testing several chips, this bit
  * does not seem to work.
  *
  * We attempt to use the bit, but fall back to the
  * timeout (which is why we do not return an error
  * on expiry) to say that the EEPROM operation has
  * completed.
  */

 while (1) {
  status = dm9000_read_locked(db, DM9000_EPCR);
//EPCR/PHY_CR
0BH):EEPROMPHY控制寄存器,
//0
ERREEEPROMPHY的访问状态。1表示EEPROMPHY正在被访问
  if ((status & EPCR_ERRE) == 0)
   break;

  msleep(1);

  if (timeout-- < 0) {
   dev_dbg(db->dev, "timeout waiting EEPROM\n");
   break;
  }
 }

 return 0;
}

/*
 *  Read a word data from EEPROM
 */
static void
dm9000_read_eeprom(board_info_t *db, int offset, u8 *to)
{
 unsigned long flags;

 if (db->flags & DM9000_PLATF_NO_EEPROM) {
  to[0] = 0xff;
  to[1] = 0xff;
  return;
 }

 mutex_lock(&db->addr_lock);

 spin_lock_irqsave(&db->lock, flags);
/*
EPAR/PHY_AR
0CH):EEPROMPHY地址寄存器
5-0
EROAEEPROM字地址或PHY寄存器地址。
EPCR/PHY_CR
0BH):EEPROMPHY控制寄存器。

EEPROM的读写:写入寄存器DM9000_EPAR的偏移地址
;写入要读写的EEPROM存储空间的地址到DM9000_EPAR
配置EPCR,选择EEPROMPHY,置读写位;
等待读写完成;到EPDRLEPDRH中取数据。
*/
 iow(db, DM9000_EPAR, offset);
 iow(db, DM9000_EPCR, EPCR_ERPRR);

 spin_unlock_irqrestore(&db->lock,flags);

 dm9000_wait_eeprom(db);

 /* delay for at-least 150uS */
 msleep(1);

 spin_lock_irqsave(&db->lock, flags);

 iow(db, DM9000_EPCR, 0x0);

 to[0] = ior(db, DM9000_EPDRL);
 to[1] = ior(db, DM9000_EPDRH);

 spin_unlock_irqrestore(&db->lock,flags);

 mutex_unlock(&db->addr_lock);
}

/*
 * Write a word data to SROM
 */
static void
dm9000_write_eeprom(board_info_t *db, int offset, u8 *data)
{
 unsigned long flags;

 if (db->flags & DM9000_PLATF_NO_EEPROM)
  return;

 mutex_lock(&db->addr_lock);

 spin_lock_irqsave(&db->lock, flags);
 iow(db, DM9000_EPAR, offset);
 iow(db, DM9000_EPDRH, data[1]);
 iow(db, DM9000_EPDRL, data[0]);
 iow(db, DM9000_EPCR, EPCR_WEP | EPCR_ERPRW);
 spin_unlock_irqrestore(&db->lock, flags);

 dm9000_wait_eeprom(db);

 mdelay(1); /* wait at least 150uS to clear */

 spin_lock_irqsave(&db->lock, flags);
 iow(db, DM9000_EPCR, 0);
 spin_unlock_irqrestore(&db->lock, flags);

 mutex_unlock(&db->addr_lock);
}

/* ethtool ops */
//
获取驱动信息
static void dm9000_get_drvinfo(struct net_device *dev,
          struct ethtool_drvinfo*info)
{
 board_info_t *dm = to_dm9000_board(dev);

 strcpy(info->driver, CARDNAME);
 strcpy(info->version, DRV_VERSION);
 strcpy(info->bus_info, to_platform_device(dm->dev)->name);
}
//
获取网络接口信息
static u32 dm9000_get_msglevel(struct net_device *dev)
{
 board_info_t *dm = to_dm9000_board(dev);

 return dm->msg_enable;
}
//
设定网络接口信息
static void dm9000_set_msglevel(struct net_device *dev, u32 value)
{
 board_info_t *dm = to_dm9000_board(dev);

 dm->msg_enable = value;
}
/*
设备无关接口的设定与信息获取最终是通过调用函数
dm9000_phy_read
dm9000_phy_write来实现的。
即是对PHY寄存器的读写。
*/
static int dm9000_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
 board_info_t *dm = to_dm9000_board(dev);

 mii_ethtool_gset(&dm->mii, cmd);
 return 0;
}

static int dm9000_set_settings(struct net_device *dev, structethtool_cmd *cmd)
{
 board_info_t *dm = to_dm9000_board(dev);

 return mii_ethtool_sset(&dm->mii, cmd);
}

static int dm9000_nway_reset(struct net_device *dev)
{
 board_info_t *dm = to_dm9000_board(dev);
 return mii_nway_restart(&dm->mii);
}
//NSR
01H):网络状态寄存器,获取网络连接状态信息
static u32 dm9000_get_link(struct net_device *dev)
{
 board_info_t *dm = to_dm9000_board(dev);
 u32 ret;

 if (dm->flags & DM9000_PLATF_EXT_PHY)
  ret = mii_link_ok(&dm->mii);
 else
  ret = dm9000_read_locked(dm, DM9000_NSR) & NSR_LINKST ? 1 : 0;

 return ret;
}

#define DM_EEPROM_MAGIC  (0x444D394B)
/*
EPAR/PHY_AR
0CH):EEPROMPHY地址寄存器,
5-0
EROAEEPROM字地址或PHY寄存器地址。 
2^6=128
*/
static int dm9000_get_eeprom_len(struct net_device *dev)
{
 return 128;
}
//
读取eeprom存储空间的值
static int dm9000_get_eeprom(struct net_device *dev,
        struct ethtool_eeprom *ee, u8 *data)
{
 board_info_t *dm = to_dm9000_board(dev);
 int offset = ee->offset;
 int len = ee->len;
 int i;

 /* EEPROM access is aligned to two bytes */

 if ((len & 1) != 0 || (offset & 1) != 0)
  return -EINVAL;

 if (dm->flags & DM9000_PLATF_NO_EEPROM)
  return -ENOENT;

 ee->magic = DM_EEPROM_MAGIC;

 for (i = 0; i < len; i += 2)
  dm9000_read_eeprom(dm, (offset + i) / 2, data + i);

 return 0;
}
//
eeprom存储空间写入数据
static int dm9000_set_eeprom(struct net_device *dev,
        struct ethtool_eeprom *ee, u8 *data)
{
 board_info_t *dm = to_dm9000_board(dev);
 int offset = ee->offset;
 int len = ee->len;
 int i;

 /* EEPROM access is aligned to two bytes */

 if ((len & 1) != 0 || (offset & 1) != 0)
  return -EINVAL;

 if (dm->flags & DM9000_PLATF_NO_EEPROM)
  return -ENOENT;

 if (ee->magic != DM_EEPROM_MAGIC)
  return -EINVAL;

 for (i = 0; i < len; i += 2)
  dm9000_write_eeprom(dm, (offset + i) / 2, data + i);

 return 0;
}
/*
ethtool
是一个实用工具,设计来给系统管理员以大量的控制网络接口的操作.
 
ethtool,
可能来控制各种接口参数,包括速度,
介质类型,
双工模式
 DMA 环设置,硬件校验和, LAN
唤醒操作,
等等
*/
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,
};
/*
NCR
00H):网络控制寄存器
*/
static void dm9000_show_carrier(board_info_t *db,
    unsigned carrier, unsigned nsr)
{
 struct net_device *ndev = db->ndev;
 unsigned ncr = dm9000_read_locked(db, DM9000_NCR);

 if (carrier)
  dev_info(db->dev, "%s: link up, %dMbps, %s-duplex, noLPA\n",
    ndev->name, (nsr & NSR_SPEED) ? 10 : 100,
    (ncr & NCR_FDX) ? "full" : "half");
 else
  dev_info(db->dev, "%s: link down\n", ndev->name);
}
/*
工作队列structdelayed_work phy_poll;的处理函数。
检测网络连接状态,通知内核,载波丢失和载波回来。
*/
static void
dm9000_poll_work(struct work_struct *w)
{
 struct delayed_work *dw = to_delayed_work(w);
 board_info_t *db = container_of(dw, board_info_t, phy_poll);
 struct net_device *ndev = db->ndev;

 if (db->flags & DM9000_PLATF_SIMPLE_PHY &&
     !(db->flags & DM9000_PLATF_EXT_PHY)) {
  unsigned nsr = dm9000_read_locked(db, DM9000_NSR);
  unsigned old_carrier = netif_carrier_ok(ndev) ? 1 : 0;
  unsigned new_carrier;

  new_carrier = (nsr & NSR_LINKST) ? 1 : 0;

  if (old_carrier != new_carrier) {
   if (netif_msg_link(db))
    dm9000_show_carrier(db, new_carrier, nsr);

   if (!new_carrier)
    netif_carrier_off(ndev);
   else
    netif_carrier_on(ndev);
  }
 } else
  mii_check_media(&db->mii, netif_msg_link(db), 0);
 
 if (netif_running(ndev))
  dm9000_schedule_poll(db);//
如果没连接上会一直检测。
}

/* dm9000_release_board
 *
 * release a board, and any mapped resources
 */

static void
dm9000_release_board(struct platform_device *pdev, struct board_info *db)
{
 /* unmap our resources */

 iounmap(db->io_addr);
 iounmap(db->io_data);//
释放映射的I/O地址空间

 /* release the resources */

 release_resource(db->data_req);
 kfree(db->data_req);

 release_resource(db->addr_req);
 kfree(db->addr_req);
}

static unsigned char dm9000_type_to_char(enum dm9000_type type)
{
 switch (type) {
 case TYPE_DM9000E: return 'e';
 case TYPE_DM9000A:return 'a';
 case TYPE_DM9000B: return 'b';
 }

 return '?';
}

/*
 *  Set DM9000 multicast address
 */
 /*
 
probe函数中有ndev->set_multicast_list= &dm9000_hash_table
 
当设备的组播列表改变和当标志改变时调用该函数。
 
 
一个组播报文是一个会被多个主机接收的网络报文,但不是所有主机.
 
这个功能通过给一组主机分配特殊的硬件地址来获得.发向一个特殊
 
地址的报文应当被那个组当中的所有主机接收.
  

  内核来跟踪在任何给定时间对哪些组播地址感兴趣.这个列表可能经常改变,
  
因为它是在任何给定时间和按照用户意愿运行的应用程序的功能
   驱动的工作是接收感兴趣的组播地址列表并递交给内核任何发向这些地址
  
的报文.
   

   MAR16H-- 1DH):多点发送地址寄存器
 */
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);
 }
//MAR
16H-- 1DH):多点发送地址寄存器(MulticastAddress Register
 
 /* 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);
}

/*
 * Initilize dm9000 board
 */
static void
dm9000_init_dm9000(struct net_device *dev)
{
 board_info_t *db = netdev_priv(dev);
 unsigned int imr;

 dm9000_dbg(db, 1, "entering %s\n", __func__);
/*
ISR
FEH):终端状态寄存器(InterruptStatus Register
7-6
IOMODE:处理器模式。0016位模式,0132位模式,
10
8位模式,00保留。
*/
 /* I/O mode */
 db->io_mode = ior(db, DM9000_ISR) >> 6; /* ISR bit7:6 keepsI/O mode */
/*
GPR
1FH):GPIO寄存器,
GPCR
1FH):GPIO控制寄存器,
GPIO0
默认为输出做POWER_DOWN功能。其它默认为输入.
*/
 /* GPIO0 on pre-activate PHY */
 iow(db, DM9000_GPR, 0); /* REG_1Fbit0 activate phyxcer */
 iow(db, DM9000_GPCR, GPCR_GEP_CNTL); /* Let GPIO0 output */
 /*
 GPCR
的第00GEPIO0:该位默认为输出1POWER_DEWN内部PHY
 
若希望启用PHY,则驱动程序需要通过写“0”PWER_DOWN信号清零。
 */
 iow(db, DM9000_GPR, 0); /* Enable PHY */

 if (db->flags & DM9000_PLATF_EXT_PHY)
  iow(db, DM9000_NCR, NCR_EXT_PHY);
/*
TCR
02H):发送控制寄存器;
BPTR
08H):背压门限寄存器;
FCR
0AH):接收/发送溢出控制寄存器;
SMCR
2FH):特殊模式控制寄存器;
*/
 /* Program operating register */
 iow(db, DM9000_TCR, 0);        /* TX Polling clear */
 /*
内部存储器空间大少16K
字节。低3K
字节单元用作发送包的缓冲区,
其他 13K
字节用作接收包的缓冲区。所以在写发送包存储区的时候,
当存储器地址越界后,自动跳回0
地址并置位 IMR
第七位。同样
在读接收包存储器的时候,当存储器地址越界后,自动跳回起始地址0x0c00 
 */
 iow(db, DM9000_BPTR, 0x3f); /*Less 3Kb, 200us */
 iow(db, DM9000_FCR, 0xff); /* Flow Control */
 iow(db, DM9000_SMCR, 0);        /*Special Mode */
 /* clear TX status */
 iow(db, DM9000_NSR, NSR_WAKEST | NSR_TX2END | NSR_TX1END);
 iow(db, DM9000_ISR, ISR_CLR_STATUS); /* Clear interrupt status */

 /* Set address filter table */
 //
设置地址过滤表,即组播地址
 dm9000_hash_table(dev);
/*
IMR
FFH):终端屏蔽寄存器.
7
PAR1使能指针自动跳回。
1
PTI1使能数据包传输终端。
0
PRI1使能数据包接收中断。

5LNKCHGI1使能连接状态改变中断。
DM9000E
没有连接状态改变中断。
*/
 imr = IMR_PAR | IMR_PTM | IMR_PRM;
 if (db->type != TYPE_DM9000E)
  imr |= IMR_LNKCHNG;

 db->imr_all = imr;

 /* Enable TX/RX interrupt mask */
 iow(db, DM9000_IMR, imr);

 /* Init Driver variable */
 db->tx_pkt_cnt = 0;
 db->queue_pkt_len = 0;
 dev->trans_start = 0;
}

/* Our watchdog timed out. Called by the networking layer */
//
传输超时时调用该函数,超时时间由watchdog设定。
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
来实现。
*/
 netif_stop_queue(dev);
 dm9000_reset(db);
 dm9000_init_dm9000(dev);
 /* We can accept TX packets again */
 /*
 
如果当前系统时间超过设备的trans_start
时间至少一个超时周期值,
 
网络层最终调用驱动的tx_timeout
方法.
 */
 dev->trans_start = jiffies;
 netif_wake_queue(dev);

 /* Restore previous register address */
 writeb(reg_save, db->io_addr);
 spin_unlock_irqrestore(&db->lock, flags);
}

/*
 *  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__);
//
一次最多只能发送两个数据包。即网卡SRAM中只能存在两个待发送的数据包。
 if (db->tx_pkt_cnt > 1)
  return 1;

 spin_lock_irqsave(&db->lock, flags);

 /* Move data to DM9000 TX RAM */
 /*
 MWCMD
F8H):存储器读地址自动增加的读数据命令.
 7-0
MWCMD:写数据到发送SRAM中,之后指向内部SRAM的读指针自动
 
增加124,根据处理器的操作模式而定(8位、16位或32位)。 
 */
 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++;//记录写入网卡SRAM中待发送的数据包的数量
 /* 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);
//
如果写入网卡SRAM中的数据包只有一个,则将数据包的长度写入TXPLLTXPLH
  /* Issue TX polling command */
//TCR
02H):发送控制寄存器。0TXREQTX(发送)请求。发送完成后自动清零该位。
  iow(db, DM9000_TCR, TCR_TXREQ); /* Cleared after TX complete*/
//
保存当前jiffies
  dev->trans_start = jiffies; /* save the time stamp */
 } else {
  /* Second packet */
//
如果当前写入的数据包不是的一个,则把该数据包的长度存入db->queue_pkt_len
  db->queue_pkt_len = skb->len;
//
只能同时存在两个待发数据包。调用函数告知网络系统不要再启动发送
  netif_stop_queue(dev);
 }

 spin_unlock_irqrestore(&db->lock, flags);

 /* free this SKB */
/*
每个数据包写入网卡SRAM后都要释放skb
如果有两个数据包要将第二个数据包的长度存入db->queue_pkt_len= skb->len
*/
 dev_kfree_skb(skb);

 return 0;
}

/*
 * 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 */
/*
 
如果网卡SRAM中还存在一个待发数据包,则将该数据包长度
 db->queue_pkt_len
写入TXPLLTXPLH中。置位发送请求。
*/  
  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;//
记录当前jiffies
  }
  netif_wake_queue(dev);
 }
}
/*
DM9000
从网络中接到一个数据包后,会在数据包前面加上4个字节,
分别为“01H”“status”(同RSR寄存器的值)、“LENL”(数据包长度低8位)、
“LENH”
(数据包长度高8位)。所以首先要读取这4个字节来确定数据包的状态,
第一个字节“01H”表示接下来的是有效数据包,若为“00H”则表示没有数据包,
若为其它值则表示网卡没有正确初始化,需要从新初始化。

这四个字节由以下结构体存储。
*/
struct dm9000_rxhdr {
 u8 RxPktReady;
 u8 RxStatus;
 __le16 RxLen;
} __attribute__((__packed__));

/*
 *  Received a packet and pass to upper layer
 */
 //
接收一个数据包存入缓存skb,并通过函数netif_rx将该缓存交给上层
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 {
  /*
  MRCMDX
F0H):存储器地址不变的读数据命令.
  
始终读取数据包的第一个字节,直到读到01H(即有效数据包)为止。
  */
  ior(db, DM9000_MRCMDX); /* Dummy read */

  /* Get most updated data */
  rxbyte = readb(db->io_data);

  /* Status check: this byte must be 0 or 1 */
  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;
  }
//
如果数据包第一字节为00H则为无效数据包。
  if (rxbyte != DM9000_PKT_RDY)
   return;

  /* A packet ready now  & Get status/length*/
  GoodPacket = true;
  //MRCMD
F2H):存储器读地址自动增加的读数据命令。
  writeb(DM9000_MRCMD, db->io_addr);
//
读取数据包的前四字节,即有效标志,接受状态,数据包长度。存于结构体rxhdr
  (db->inblk)(db->io_data, &rxhdr, sizeof(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) {//
一个数据包的长度应大于64字节
   GoodPacket = false;
   if (netif_msg_rx_err(db))
    dev_dbg(db->dev, "RX: Bad Packet(runt)\n");
  }

  if (RxLen > DM9000_PKT_MAX) {//数据包长度不应大于1.5K
   dev_dbg(db->dev, "RST: RX Len:%x\n", RxLen);
  }

  /* rxhdr.RxStatus is identical to RSR register. */
  //rxhdr.RxStatus
的值既是RSR06H):接收状态寄存器
  //
对各种错误进行判断和记录。
  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 */
  //
如果是一个好的数据包则分配skb结构体,和足够缓存,并将数据读入缓存
  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 */
/*
这个函数抽取协议标识(ETH_P_IP,
在这个情况下)从以太网头
它也赋值skb->mac.raw,
从报文data (使用skb_pull)去掉硬件头部,
 
并且设置skb->pkt_type.
最后一项在skb
分配是缺省为PACKET_HOST
 (
指示报文是发向这个主机的),eth_type_trans
改变它来反映以太网目的地址
 如果这个地址不匹配接收它的接口地址,pkt_type
成员被设为 
 PACKET_OTHERHOST.
结果,除非接口处于混杂模式或者内核打开了报文转发
 netif_rx 丢弃任何类型为PACKET_OTHERHOST
的报文
 
//union { /* ... */// } h; 
//union { /* ... */ } nh; 
//union { /*... */} mac; 
//指向报文中包含的各级的头的指针.包含在结构体structnet_device *dev;

*/   
   skb->protocol = eth_type_trans(skb, dev);
/*
递交 socket缓存给上层.
实际上netif_rx
返回一个整数
NET_RX_SUCCESS(0) 意思是报文成功接收;任何其他值指示错误
3 个返回值(NET_RX_CN_LOW, NET_RX_CN_MOD,
NET_RX_CN_HIGH )
指出网络子系统的递增的拥塞级别;NET_RX_DROP
意思是报文被丢弃
*/
   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);//
如果是有效数据包则退出
}
/*
在一个数据包发送完,一个数据包接收到,网络链路状态改变,触发中断,调用
该中断处理函数。
在非中断模式下,被函数dm9000_poll_controller调用。
*/
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 bloc

抱歉!评论已关闭.