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 ---------------- */
//在EEPROM或PHY地址寄存器中要选择内部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):EEPROM和PHY控制寄存器,
//0:ERRE:EEPROM或PHY的访问状态。1表示EEPROM或PHY正在被访问
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):EEPROM或PHY地址寄存器
5-0:EROA:EEPROM字地址或PHY寄存器地址。
EPCR/PHY_CR(0BH):EEPROM和PHY控制寄存器。
对EEPROM的读写:写入寄存器DM9000_EPAR的偏移地址
;写入要读写的EEPROM存储空间的地址到DM9000_EPAR;
配置EPCR,选择EEPROM和PHY,置读写位;
等待读写完成;到EPDRL和EPDRH中取数据。
*/
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):EEPROM或PHY地址寄存器,
5-0:EROA:EEPROM字地址或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
当设备的组播列表改变和当标志改变时调用该函数。
一个组播报文是一个会被多个主机接收的网络报文,但不是所有主机.
这个功能通过给一组主机分配特殊的硬件地址来获得.发向一个特殊
地址的报文应当被那个组当中的所有主机接收.
内核来跟踪在任何给定时间对哪些组播地址感兴趣.这个列表可能经常改变,
因为它是在任何给定时间和按照用户意愿运行的应用程序的功能.
驱动的工作是接收感兴趣的组播地址列表并递交给内核任何发向这些地址
的报文.
MAR(16H-- 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:处理器模式。00为16位模式,01为32位模式,
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的第0位0:GEPIO0:该位默认为输出1到POWER_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:PAR:1使能指针自动跳回。
1:PTI:1使能数据包传输终端。
0:PRI:1使能数据包接收中断。
5:LNKCHGI:1使能连接状态改变中断。
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的读指针自动
增加1、2或4,根据处理器的操作模式而定(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中的数据包只有一个,则将数据包的长度写入TXPLL和TXPLH中
/* Issue TX polling command */
//TCR(02H):发送控制寄存器。0:TXREQ:TX(发送)请求。发送完成后自动清零该位。
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写入TXPLL和TXPLH中。置位发送请求。
*/
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的值既是RSR(06H):接收状态寄存器
//对各种错误进行判断和记录。
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 block IRQs */
spin_lock_irqsave(&db->lock, flags);
/* Save previous register address */
reg_save = readb(db->io_addr);
/* Disable all interrupts */
//IMR(FFH):终端屏蔽寄存器。屏蔽所有中断。
iow(db, DM9000_IMR, IMR_PAR);
/* Got DM9000 interrupt status */
/*
ISR(FEH):终端状态寄存器.
ISR寄存器各状态写1清除.
*/
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 */
//一个中断号可能被多个中断源触发,所以要判断是哪个中断源。
if (int_status & ISR_PRS)
dm9000_rx(dev);
/* Trnasmit Interrupt check */
if (int_status & ISR_PTS)
dm9000_tx_done(dev, db);
//DM9000E在链路状态发生改变时不触发中断
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;
}
#ifdef CONFIG_NET_POLL_CONTROLLER
/*
*Used by netconsole
*/
/*
实现 netpoll的驱动应当实现 poll_controller方法.
它的工作是跟上控制器上可能发生的任何东西,在缺乏设备中断时.
几乎所有的poll_controller方法采用下面形式:
*/
static void dm9000_poll_controller(struct net_device *dev)
{
disable_irq(dev->irq);
dm9000_interrupt(dev->irq, dev);
enable_irq(dev->irq);
}
#endif
/*
* Open the interface.
* The interface is opened whenever "ifconfig" actives it.
*/
//在接口能够承载报文前内核必须打开它并分配一个地址给它.
// 内核打开或者关闭一个接口对应ifconfig命令
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 somethingthat
* may work, and tell the user that this is a problem */
if (irqflags == IRQF_TRIGGER_NONE)
dev_warn(db->dev, "WARNING: no IRQ resource flagsset.\n");
irqflags |= IRQF_SHARED;
//注册一个中断,中断处理函数为dm9000_interrupt,传入参数dev。
if (request_irq(dev->irq, &dm9000_interrupt, irqflags,dev->name, dev))
return -EAGAIN;
/* Initialize DM9000 board */
dm9000_reset(db);
dm9000_init_dm9000(dev);
/* Init driver variable */
db->dbug_cnt = 0;
/*
假如发生链路变化的情况,需要检查介质无关接口(MII)
的载波状态是否同样也发生变化,否则就要准备重新启动MII接口
*/
mii_check_media(&db->mii, netif_msg_link(db), 1);
netif_start_queue(dev);//启动接口的发送队列(允许它接受发送报文)
//如果DM9000E触发工作队列structdelayed_work phy_poll;
//检测网络连接状态是否发生变化
dm9000_schedule_poll(db);
return 0;
}
/*
* Sleep, either by using msleep() or if we are suspending, then
* use mdelay() to sleep.
*/
static void dm9000_msleep(board_info_t *db, unsigned int ms)
{
if (db->in_suspend)
mdelay(ms);
else
msleep(ms);
}
/*
* Read a word from phyxcer
*/
/*
在DM9000(A)中,还有一些PHY寄存器,也称之为介质无关接口MII寄存器,
需要我们去访问。这些寄存器是字对齐的,即16位宽。
独立于媒介的接口支持,对函数mdio_read和mdio_write的实现最终调用
函数dm9000_phy_read和dm9000_phy_write。
*/
static int
dm9000_phy_read(struct net_device *dev, int phy_reg_unused, int reg)
{
board_info_t *db = netdev_priv(dev);
unsigned long flags;
unsigned int reg_save;
int ret;
mutex_lock(&db->addr_lock);
spin_lock_irqsave(&db->lock,flags);
/* Save previous register address */
reg_save = readb(db->io_addr);
/* Fill the phyxcer register into REG_0C */
/*
EPAR/PHY_AR(0CH):EEPROM或PHY地址寄存器.
5-0:EROA:EEPROM字地址或PHY寄存器地址。
EPCR/PHY_CR(0BH):EEPROM和PHY控制寄存器.
对PHY寄存器的操作:写入寄存器DM9000_EPAR的偏移地址;
写入将要操作PHY寄存器的偏移地址到寄存器EPAR;
写入寄存器DM9000_EPCR的偏移地址;
置寄存器EPCR中的读命令位;
等待数据被读到寄存器EPDRH和EPDRL中。
*/
iow(db, DM9000_EPAR, DM9000_PHY | reg);
iow(db, DM9000_EPCR, EPCR_ERPRR | EPCR_EPOS); /*Issue phyxcer read command */
writeb(reg_save, db->io_addr);
spin_unlock_irqrestore(&db->lock,flags);
dm9000_msleep(db, 1); /* Wait read complete */
spin_lock_irqsave(&db->lock,flags);
reg_save = readb(db->io_addr);
//清除读命令标志位
iow(db, DM9000_EPCR, 0x0); /* Clear phyxcer read command */
/* The read data keeps on REG_0D & REG_0E */
ret = (ior(db, DM9000_EPDRH) << 8) | ior(db, DM9000_EPDRL);
/* restore the previous address */
writeb(reg_save, db->io_addr);
spin_unlock_irqrestore(&db->lock,flags);
mutex_unlock(&db->addr_lock);
dm9000_dbg(db, 5, "phy_read[%02x] -> %04x\n",reg, ret);
return ret;
}
/*
* Write a word to phyxcer
*/
static void
dm9000_phy_write(struct net_device *dev,
int phyaddr_unused, int reg, int value)
{
board_info_t *db = netdev_priv(dev);
unsigned long flags;
unsigned long reg_save;
dm9000_dbg(db, 5, "phy_write[%02x] = %04x\n",reg, value);
mutex_lock(&db->addr_lock);
spin_lock_irqsave(&db->lock,flags);
/* Save previous register address */
reg_save = readb(db->io_addr);
/* Fill the phyxcer register into REG_0C */
iow(db, DM9000_EPAR, DM9000_PHY | reg);
/* Fill the written data into REG_0D & REG_0E */
iow(db, DM9000_EPDRL, value);
iow(db, DM9000_EPDRH, value >> 8);
iow(db, DM9000_EPCR, EPCR_EPOS | EPCR_ERPRW); /*Issue phyxcer write command */
writeb(reg_save, db->io_addr);
spin_unlock_irqrestore(&db->lock, flags);
dm9000_msleep(db, 1); /* Wait write complete */
spin_lock_irqsave(&db->lock,flags);
reg_save = readb(db->io_addr);
iow(db, DM9000_EPCR, 0x0); /* Clear phyxcer writecommand */
/* restore the previous address */
writeb(reg_save, db->io_addr);
spin_unlock_irqrestore(&db->lock, flags);
mutex_unlock(&db->addr_lock);
}
static void
dm9000_shutdown(struct net_device *dev)
{
board_info_t *db = netdev_priv(dev);
/* RESET device */
//BMCR(00H):基本模式控制寄存器
//15:reset:1PHY软件复位,0正常操作。复位操作使PHY寄存器的值为默认值。
//复位操作完成后,该位自动清零。
dm9000_phy_write(dev, 0, MII_BMCR, BMCR_RESET); /* PHY RESET */
iow(db, DM9000_GPR, 0x01); /* Power-Down PHY */
iow(db, DM9000_IMR, IMR_PAR); /* Disable all interrupt */
iow(db, DM9000_RCR, 0x00); /* Disable RX */
}
/*
* 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);
netif_stop_queue(ndev);//告知网络系统不要再启动发送。
netif_carrier_off(ndev);//告知内核设备载波丢失。
/* free interrupt */
free_irq(ndev->irq, ndev);
dm9000_shutdown(ndev);
return 0;
}
#define res_size(_r) (((_r)->end - (_r)->start) + 1)
/*
* Search DM9000 board, allocate space and register it
*/
static int __devinit
dm9000_probe(struct platform_device *pdev)
{
/*
结构体dm9000_plat_data在文件mach-tq2440.c中,形式如下
static struct dm9000_plat_data s3c_dm9k_platdata= {
.flags = DM9000_PLATF_16BITONLY,
};
*/
struct dm9000_plat_data *pdata = pdev->dev.platform_data;
struct board_info *db; /* Point a board information structure */
struct net_device *ndev;
const unsigned char *mac_src;
int ret = 0;
int iosize;
int i;
u32 id_val;
#if defined(CONFIG_ARCH_S3C2410)
unsigned int oldval_bwscon = *(volatile unsigned int *)S3C2410_BWSCON;
unsigned int oldval_bankcon4 = *(volatile unsigned int *)S3C2410_BANKCON4;
#endif
/* Init network device */
//这个函数分配一个网络设备使用eth%d作为参数name.它提供了自己的
// 初始化函数( ether_setup )来设置几个net_device字段,
//使用对以太网设备合适的值.
ndev = alloc_etherdev(sizeof(struct board_info));
if (!ndev) {
dev_err(&pdev->dev, "could not allocatedevice.\n");
return -ENOMEM;
}
//#define SET_NETDEV_DEV(net, pdev) ((net)->dev.parent = (pdev))
SET_NETDEV_DEV(ndev, &pdev->dev);
dev_dbg(&pdev->dev, "dm9000_probe()\n");
//对总线操作时序做相应改变。
#if defined(CONFIG_ARCH_S3C2410)
*((volatile unsigned int *)S3C2410_BWSCON)= (oldval_bwscon & ~(3<<16)) | S3C2410_BWSCON_DW4_16| S3C2410_BWSCON_WS4 | S3C2410_BWSCON_ST4;
*((volatile unsigned int *)S3C2410_BANKCON4)= 0x1f7c;
#endif
/* setup board info structure */
db = netdev_priv(ndev);//对netdev私有成员操作函数
memset(db, 0, sizeof(*db));
db->dev = &pdev->dev;
db->ndev = ndev;
spin_lock_init(&db->lock);
mutex_init(&db->addr_lock);
//提交一个任务给一个工作队列,你需要填充一个work_struct结构db->phy_poll
INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work);
//获取硬件资源与中断资源。
db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
db->irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (db->addr_res == NULL || db->data_res == NULL ||
db->irq_res == NULL) {
dev_err(db->dev, "insufficient resources\n");
ret = -ENOENT;
goto out;
}
iosize = res_size(db->addr_res);
//分配I/O内存区.
//这个函数分配一个len字节的内存区,从 start开始.
db->addr_req = request_mem_region(db->addr_res->start, iosize,
pdev->name);
if (db->addr_req == NULL) {
dev_err(db->dev, "cannot claim address reg area\n");
ret = -EIO;
goto out;
}
/*
ioremap 建立新页表;不同于vmalloc,但是,它实际上不分配任何内存.
ioremap 的返回值是一个特殊的虚拟地址可用来存取特定的物理地址范围;
获得的虚拟地址应当最终通过调用iounmap来释放.
在许多系统,I/O 内存根本不是可以这种方式直接存取的.
因此必须首先设置一个映射.这是ioremap函数的功能
*/
db->io_addr = ioremap(db->addr_res->start, iosize);
if (db->io_addr == NULL) {
dev_err(db->dev, "failed to ioremap address reg\n");
ret = -EINVAL;
goto out;
}
iosize = res_size(db->data_res);
db->data_req = request_mem_region(db->data_res->start, iosize,
pdev->name);
if (db->data_req == NULL) {
dev_err(db->dev, "cannot claim data reg area\n");
ret = -EIO;
goto out;
}
db->io_data = ioremap(db->data_res->start,iosize);
if (db->io_data == NULL) {
dev_err(db->dev, "failed to ioremap data reg\n");
ret = -EINVAL;
goto out;
}
/* fill in parameters for net-dev structure */
ndev->base_addr = (unsigned long)db->io_addr;
ndev->irq = db->irq_res->start;
/* ensure at least we have a default set of IO routines */
//根据iosize来设定网卡I/O数据线宽度,和I/O操作函数。
dm9000_set_io(db, iosize);
/* check to see if anything is being over-ridden */
if (pdata != NULL) {
/* check to see if the driver wants to over-ride the
* default IO width */
if (pdata->flags & DM9000_PLATF_8BITONLY)
dm9000_set_io(db, 1);
if (pdata->flags & DM9000_PLATF_16BITONLY)
dm9000_set_io(db, 2);
if (pdata->flags & DM9000_PLATF_32BITONLY)
dm9000_set_io(db, 4);
/* check to see if there are any IO routine
* over-rides */
if(pdata->inblk != NULL)
db->inblk = pdata->inblk;
if (pdata->outblk != NULL)
db->outblk = pdata->outblk;
if (pdata->dumpblk != NULL)
db->dumpblk = pdata->dumpblk;
db->flags= pdata->flags;
}
#ifdef CONFIG_DM9000_FORCE_SIMPLE_PHY_POLL
db->flags |= DM9000_PLATF_SIMPLE_PHY;
#endif
dm9000_reset(db);
/* try multiple times, DM9000 sometimes gets the readwrong */
/*
VID(28H-- 29H):生产厂家序列号(VendorID)
7-0:VIDL:低半字节(28H),只读,默认46H。
7-0:VIDH:高半字节(29H),只读,默认0AH。
PID(2AH--2BH):产品序列号(ProductID)
7-0:PIDL:低半字节(2AH),只读,默认00H。
7-0:PIDH:高半字节(2BH),只读,默认90H。
*/
for (i = 0; i < 8; i++) {
id_val = ior(db, DM9000_VIDL);
id_val |= (u32)ior(db, DM9000_VIDH) << 8;
id_val |= (u32)ior(db, DM9000_PIDL) << 16;
id_val |= (u32)ior(db, DM9000_PIDH) << 24;
//#define DM9000_ID 0x90000A46
if (id_val == DM9000_ID)
break;
dev_err(db->dev, "read wrong id 0x%08x\n", id_val);
}
if (id_val != DM9000_ID) {
dev_err(db->dev, "wrong id: 0x%08x\n", id_val);
ret = -ENODEV;
goto out;
}
/* Identify what type of DM9000 we are working on */
//CHIPR(2CH):芯片修订版本(CHIPRevision)
id_val = ior(db, DM9000_CHIPR);
dev_dbg(db->dev, "dm9000 revision 0x%02x\n", id_val);
switch (id_val) {
case CHIPR_DM9000A:
db->type = TYPE_DM9000A;
break;
case CHIPR_DM9000B:
db->type = TYPE_DM9000B;
break;
default:
dev_dbg(db->dev, "ID %02x => defaulting toDM9000E\n", id_val);
db->type = TYPE_DM9000E;
}
/* from this point we assume that we have found a DM9000*/
/* driver system function */
//初始化函数,来设置几个net_device字段,使用对以太网设备合适的值
ether_setup(ndev);
ndev->open = &dm9000_open;
ndev->hard_start_xmit = &dm9000_start_xmit;
ndev->tx_timeout =&dm9000_timeout;
ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
ndev->stop = &dm9000_stop;
ndev->set_multicast_list = &dm9000_hash_table;//组播
/*
对ethtool 支持的相关声明可在<linux/ethtool.h>中找到.
它的核心是一个ethtool_ops类型的结构,里面包含一个全部
24 个不同方法来支持ethtool
*/
ndev->ethtool_ops = &dm9000_ethtool_ops;
ndev->do_ioctl = &dm9000_ioctl;
#ifdef CONFIG_NET_POLL_CONTROLLER
ndev->poll_controller = &dm9000_poll_controller;
#endif
db->msg_enable =NETIF_MSG_LINK;
//PHY地址寄存器,5-0:EROA:EEPROM字地址或PHY寄存器地址。
db->mii.phy_id_mask = 0x1f;
db->mii.reg_num_mask = 0x1f;
db->mii.force_media = 0;
db->mii.full_duplex = 0;
db->mii.dev = ndev;
//对介质无关接口的支持
db->mii.mdio_read = dm9000_phy_read;
db->mii.mdio_write = dm9000_phy_write;
#if defined(CONFIG_ARCH_S3C2410)
printk("Now use the default MAC address: 10:23:45:67:89:ab\n");
mac_src = "EmbedSky";//设置默认MAC地址
ndev->dev_addr[0] = 0x10;
ndev->dev_addr[1] = 0x23;
ndev->dev_addr[2] = 0x45;
ndev->dev_addr[3] = 0x67;
ndev->dev_addr[4] = 0x89;
ndev->dev_addr[5] = 0xab;
#else
mac_src = "eeprom";//MAC地址来源
//在eeprom的0~5这六字节里放着MAC地址
/* try reading the node address from the attached EEPROM */
for (i = 0; i < 6; i += 2)
dm9000_read_eeprom(db, i / 2, ndev->dev_addr+i);
if (!is_valid_ether_addr(ndev->dev_addr) &&pdata != NULL) {
mac_src = "platform data";
memcpy(ndev->dev_addr, pdata->dev_addr, 6);
}
if (!is_valid_ether_addr(ndev->dev_addr)) {
/* try reading from mac */
/*
PAR(10H --15H):物理地址(MAC)寄存器(PhysicalAddress Register)
7-0:PAD0 --PAD5:物理地址字节0--字节5(10H --15H)。
用来保存6个字节的MAC地址。
*/
mac_src = "chip";
for (i = 0; i < 6; i++)
ndev->dev_addr[i] = ior(db, i+DM9000_PAR);
}
if (!is_valid_ether_addr(ndev->dev_addr))
dev_warn(db->dev, "%s: Invalid ethernet MAC address. Please"
"set using ifconfig\n", ndev->name);
#endif
//以下设置以便在suspend,resume,remove等函数中通过
//struct net_device *ndev = platform_get_drvdata(pdev);获取ndev。
platform_set_drvdata(pdev, ndev);
//在你调用register_netdev时,你的驱动可能会马上被调用来操作设备.
// 因此,你不应当注册设备直到所有东西都已经完全初始化
ret = register_netdev(ndev);
if (ret == 0)
printk(KERN_INFO "%s: dm9000%c at %p,%p IRQ %d MAC: %pM(%s)\n",
ndev->name,dm9000_type_to_char(db->type),
db->io_addr,db->io_data, ndev->irq,
ndev->dev_addr, mac_src);
return 0;
out:
#if defined(CONFIG_ARCH_S3C2410)
*(volatile unsigned int *)S3C2410_BWSCON = oldval_bwscon;
*(volatile unsigned int *)S3C2410_BANKCON4= oldval_bankcon4;
#endif
dev_err(db->dev, "not found (%d).\n", ret);
dm9000_release_board(pdev, db);
free_netdev(ndev);
return ret;
}
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;
}
static int
dm9000_drv_resume(struct platform_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;
}
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 freeddevice\n");
return 0;
}
//填充平台设备结构体platform_driver
static struct platform_driver dm9000_driver = {
.driver = {
.name = "dm9000",
.owner = THIS_MODULE,
},
.probe = dm9000_probe,
.remove = __devexit_p(dm9000_drv_remove),
.suspend = dm9000_drv_suspend,
.resume = dm9000_drv_resume,
};
static int __init
dm9000_init(void)
{
printk(KERN_INFO "%s Ethernet Driver, V%s\n", CARDNAME,DRV_VERSION);
returnplatform_driver_register(&dm9000_driver);
}
static void __exit
dm9000_cleanup(void)
{
platform_driver_unregister(&dm9000_driver);
}
module_init(dm9000_init);
module_exit(dm9000_cleanup);
MODULE_AUTHOR("Sascha Hauer, Ben Dooks");
MODULE_DESCRIPTION("Davicom DM9000 network driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:dm9000");