本人阅读的DM9000驱动程序是2.6.32.1内核的,较以前的内核驱动程序发生了较大的差别。用于定时检测连接状态的定时器被定时工作队列替代,net_device结构体中的操作函数以前需要分别赋值而现在全归列到struct net_device_ops结构体中,并且在驱动中增加了支持使用ethool工具内核调用函数来支持ethool工具。
struct delayed_work {
struct work_struct work;
struct timer_list timer;
};即由工作队列和定时器组合而成的,有关其得初始化和操作函数如下:
#define INIT_DELAYED_WORK(_work, _func) /
do { INIT_WORK(&(_work)->work, (_func)); init_timer(&(_work)->timer); } while (0) ;对应于工作队列的调度函数其调度函数为schedule_delayed_work---queue_delayed_work,后者函数如下:
int queue_delayed_work(struct workqueue_struct *wq,struct delayed_work *dwork,
unsigned long delay)
{
if (delay == 0)
return queue_work(wq, &dwork->work); //没有定时时间此为普通调度工作队列
return queue_delayed_work_on(-1, wq, dwork, delay);
}
int queue_delayed_work_on(int cpu, struct workqueue_struct *wq,struct delayed_work *dwork,
unsigned long delay)
{
int ret = 0;
struct timer_list *timer = &dwork->timer;
struct work_struct *work = &dwork->work;
if (!test_and_set_bit(WORK_STRUCT_PENDING, work_data_bits(work))) {
BUG_ON(timer_pending(timer));
BUG_ON(!list_empty(&work->entry));
timer_stats_timer_set_start_info(&dwork->timer);//空函数
set_wq_data(work, wq_per_cpu(wq, raw_smp_processor_id()));
timer->expires = jiffies + delay;//初始化定时器
timer->data = (unsigned long)dwork;//处理函数参数
timer->function = delayed_work_timer_fn;//定时器处理函数
if (unlikely(cpu >= 0))
add_timer_on(timer, cpu);
else
add_timer(timer);//启动内核定时器
ret = 1;
}
return ret;
}
static void delayed_work_timer_fn(unsigned long __data)//定时器处理函数
{
struct delayed_work *dwork = (struct delayed_work *)__data;
struct cpu_workqueue_struct *cwq = get_wq_data(&dwork->work);
struct workqueue_struct *wq = cwq->wq; //以上操作为获取工作队列结构
__queue_work(wq_per_cpu(wq, smp_processor_id()), &dwork->work);
//将定义的工作节点插入工作队列,此为调度语句的实质操作语句
} 使用定时工作队列的实质是在其初始化且调度后,在定时器处理函数里调度工作队列,将工作节点加入工作队列中从而调用工作队列处理函数来检测网络连接状态。
DM9000的读数据包保存在0C00-4000 ,写数据包保存在0000-0BFF中;因为里面没有关于dma的任何处理。
事实上,如果支持外部dma的话,从管脚来看,是需要DMA request和DMA acknowledgement两个管脚的。事实上DM9000的PIN描述里没有。
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>
#include "dm9000.h"
#define DM9000_PHY 0x40
//PHY Address bit 1 and 0, the PHY address bit [4:2] is force to 0.
#define CARDNAME "dm9000"
#define DRV_VERSION "1.31"
//传输时间超出则看门狗溢出
static int watchdog = 5000;
module_param(watchdog, int, 0400);
MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds");
enum dm9000_type {
TYPE_DM9000E, /* original DM9000 */
TYPE_DM9000A,
TYPE_DM9000B
};
//装载网卡信息的私有数据结构
typedef struct board_info {
void __iomem *io_addr; //寄存器io首地址
void __iomem *io_data; //数据IO地址
u16 irq; //中断号
u16 tx_pkt_cnt;//发送数据包计数
u16 queue_pkt_len;//发送队列数据包的长度
u16 queue_start_addr;
u16 queue_ip_summed;//队列中发送包中IP包的校验和,长度和校验和要保存起来给发送函数用
u16 dbug_cnt; //调试次数
u8 io_mode; /* 0:word, 2:byte */
u8 phy_addr;
u8 imr_all;//中断使能值
unsigned int flags; //工作标志DM9000_PLATF_EXT_PHY等
unsigned int in_suspend :1;//设备挂起标志
int debug_level;//调试等级
enum dm9000_type type;//芯片类型
void (*inblk)(void __iomem *port, void *data, int length);//读取IO口函数
void (*outblk)(void __iomem *port, void *data, int length);//写入IO口寒素
void (*dumpblk)(void __iomem *port, int length);
struct device *dev; //父设备
struct resource *addr_res; //地址资源
struct resource *data_res; //数据资源
struct resource *addr_req; //addr_res申请的IO内存
struct resource *data_req; //data_res申请的IO内存
struct resource *irq_res; //中断资源
struct mutex addr_lock; /* phy and eeprom access lock */
struct delayed_work phy_poll;//定时的工作队列
struct net_device *ndev;//对应的网络设备
spinlock_t lock; //锁
struct mii_if_info mii;//MII接口控制芯片信息
u32 msg_enable;//调试信息等级值
int rx_csum;//接受数据校验和使能
int can_csum;//是否校验和功能
int ip_summed;//发送包中IP包的校验和
} 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);
}
static void
dm9000_reset(board_info_t * db)//复位芯片
{
dev_dbg(db->dev, "resetting device/n");
writeb(DM9000_NCR, db->io_addr);
udelay(200);
writeb(NCR_RST, db->io_data);//写1复位
udelay(200);//10U后自动清零
}
static u8
ior(board_info_t * db, int reg)//读取寄存器
{
writeb(reg, db->io_addr);//寄存器地址为索引值写入
return readb(db->io_data);//从数据端口读出寄存器数据
}
static void
iow(board_info_t * db, int reg, int value)//写寄存器
{
writeb(reg, db->io_addr);
writeb(value, db->io_data);
}
//写数据到SRAM
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);
}
//读取SRAM中数据
static void dm9000_inblk_8bit(void __iomem *reg, void *data, int count)//读字节
{
readsb(reg, data, count);
}
static void dm9000_inblk_16bit(void __iomem *reg, void *data, int count)//读字
{
readsw(reg, data, (count+1) >> 1);//为5字节时需读3个字故需加1
}
static void dm9000_inblk_32bit(void __iomem *reg, void *data, int count)//读双字
{
readsl(reg, data, (count+3) >> 2);
}
//将数据读出但不保存
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);
}
static void dm9000_set_io(struct board_info *db, int byte_width)//设置IO位宽
{
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 to 16bit/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;
}
}
static void dm9000_schedule_poll(board_info_t *db)//调度定时工作队列
{
if (db->type == TYPE_DM9000E)//为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);//通用的MII接口命令函数
}
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);
return ret;
}
static int dm9000_wait_eeprom(board_info_t *db)//检测EEPROM操作完否?
{
unsigned int status;
int timeout = 8; /* wait max 8msec */
while (1) {
status = dm9000_read_locked(db, DM9000_EPCR);
if ((status & EPCR_ERRE) == 0)//操作完成时退出
break;
msleep(1);
if (timeout-- < 0) {
dev_dbg(db->dev, "timeout waiting EEPROM/n");
break;
}
}
return 0;
}
static void
dm9000_read_eeprom(board_info_t *db, int offset, u8 *to)//读取eeprom一个字
{
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);
iow(db, DM9000_EPAR, offset);//写入EEPROM Word Address
iow(db, DM9000_EPCR, EPCR_ERPRR);//EEPROM Read
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);//恢复上电值0
to[0] = ior(db, DM9000_EPDRL);
to[1] = ior(db, DM9000_EPDRH);//读出值
spin_unlock_irqrestore(&db->lock, flags);
mutex_unlock(&db->addr_lock);
}
static void
dm9000_write_eeprom(board_info_t *db, int offset, u8 *data)//写入eeprom一个字
{
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);//恢复上电值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;
}
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, struct ethtool_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);
}
static uint32_t dm9000_get_rx_csum(struct net_device *dev)//读取硬件读校验和使能状态
{
board_info_t *dm = to_dm9000_board(dev);
return dm->rx_csum;
}
static int dm9000_set_rx_csum(struct net_device *dev, uint32_t data)//设置硬件读检验功能(AB型有)
{
board_info_t *dm = to_dm9000_board(dev);
unsigned long flags;
if (dm->can_csum) {
dm->rx_csum = data;
spin_lock_irqsave(&dm->lock, flags);
iow(dm, DM9000_RCSR, dm->rx_csum ? RCSR_CSUM : 0);
spin_unlock_irqrestore(&dm->lock, flags);
return 0;
}
return -EOPNOTSUPP;
}
static int dm9000_set_tx_csum(struct net_device *dev, uint32_t data)
{
board_info_t *dm = to_dm9000_board(dev);
int ret = -EOPNOTSUPP;
if (dm->can_csum)
ret = ethtool_op_set_tx_csum(dev, data);
return ret;
}
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)
static int dm9000_get_eeprom_len(struct net_device *dev)
{
return 128;
}
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;
}
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;
}
//使用ethool工具内核调用函数
static const struct ethtool_ops dm9000_ethtool_ops = {
.get_drvinfo = dm9000_get_drvinfo,//Report driver information
.get_settings = dm9000_get_settings,//Get device-specific settings
.set_settings = dm9000_set_settings,//Set device-specific settings
.get_msglevel = dm9000_get_msglevel,//Report driver message level
.set_msglevel = dm9000_set_msglevel,//Set driver message level
.nway_reset = dm9000_nway_reset,//Restart autonegotiation
.get_link = dm9000_get_link,//Get link status
.get_eeprom_len = dm9000_get_eeprom_len,//得到eeprom长度
.get_eeprom = dm9000_get_eeprom,//Read data from the device EEPROM
.set_eeprom = dm9000_set_eeprom,//Write data to the device EEPROM
.get_rx_csum = dm9000_get_rx_csum,//Report whether receive checksums are turned on or off
.set_rx_csum = dm9000_set_rx_csum,//Turn receive checksum on or off
.get_tx_csum = ethtool_op_get_tx_csum,//Report whether transmit checksums are turned on or off
.set_tx_csum = dm9000_set_tx_csum,//Turn transmit checksums on or off
};
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);//读取NCR
if (carrier)
dev_info(db->dev, "%s: link up, %dMbps, %s-duplex, no LPA/n",
ndev->name, (nsr & NSR_SPEED) ? 10 : 100,
(ncr & NCR_FDX) ? "full" : "half");
else
dev_info(db->dev, "%s: link down/n", ndev->name);
}
static void
dm9000_poll_work(struct work_struct *w)//工作队列处理函数,主要测试网络连接状态
{
struct delayed_work *dw = to_delayed_work(w);//获取delayed_work
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)) {//使用片内PHY而非片外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;//仅适合于使用内部PHY,测试网卡是否连接
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//使用片外phy芯片时
mii_check_media(&db->mii, netif_msg_link(db), 0);//检测MII双工模式是否改变
if (netif_running(ndev))//检测设备是否运行
dm9000_schedule_poll(db);
}
static void
dm9000_release_board(struct platform_device *pdev, struct board_info *db)//释放资源
{
/* unmap our resources */
iounmap(db->io_addr);
iounmap(db->io_data);
/* 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 '?';
}
static void
dm9000_hash_table(struct net_device *dev)//设置组播地址
{
board_info_t *db = netdev_priv(dev);
struct dev_mc_list *mcptr = dev->mc_list;//广播MAC地址
int mc_cnt = dev->mc_count;//mc_list项目数
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]);//将组播地址写入Physical Address Register
for (i = 0; i < 4; i++)/* Clear Hash Table */
hash_table[i] = 0x0;
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;//计算以太网CRC校验多项式的位元?
hash_table[hash_val / 16] |= (u16) 1 << (hash_val % 16);//设置位元处bit为1
}
for (i = 0, oft = DM9000_MAR; i < 4; i++) {//保存哈希表到Multicast Address Register
iow(db, oft++, hash_table[i]);
iow(db, oft++, hash_table[i] >> 8);
}
iow(db, DM9000_RCR, rcr);
//RX Enable|Discard CRC Error Packet|Discard Long Packet 1522byte
spin_unlock_irqrestore(&db->lock, flags);
}
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__);
db->io_mode = ior(db, DM9000_ISR) >> 6; //I/O mode
dm9000_set_rx_csum(dev, db->rx_csum);//使能硬件读校验和
iow(db, , 0); //GEPIO0作为IO口
iow(db, DM9000_GDM9000_GPRPCR, GPCR_GEP_CNTL); /*PIO0 output */
iow(db, DM9000_GPR, 0); //写0激活PHY
if (db->flags & DM9000_PLATF_EXT_PHY)//使用片外PHY时
iow(db, DM9000_NCR, NCR_EXT_PHY);
iow(db, DM9000_TCR, 0); /* TX Control Register--TX Polling clear */
iow(db, DM9000_BPTR, 0x37); /* 3Kb, 200us *///回环界限定义
iow(db, DM9000_FCR, 0xff); //流控制
iow(db, DM9000_SMCR, 0); /* Special Mode */
iow(db, DM9000_NSR, NSR_WAKEST | NSR_TX2END | NSR_TX1END);/* clear TX status */
iow(db, DM9000_ISR, ISR_CLR_STATUS);
/* Clear interrupt status 即ISR_ROOS | ISR_ROS | ISR_PTS | ISR_PRS*/
/* Set address filter table */
dm9000_hash_table(dev);
imr = IMR_PAR | IMR_PTM | IMR_PRM;//数据包接受/发送完后中断,SRAM指针自动回首地址
if (db->type != TYPE_DM9000E)
imr |= IMR_LNKCHNG;//为AB型时标记连接状态改变中断
db->imr_all = imr;
iow(db, DM9000_IMR, imr);/*Enable TX/RX interrupt*/
/* Init Driver variable */
db->tx_pkt_cnt = 0;
db->queue_pkt_len = 0;
dev->trans_start = 0;//最后成功发送时间为0
}
//发送超时处理函数
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(dev);//停止队列
dm9000_reset(db);
dm9000_init_dm9000(dev);//初始化
dev->trans_start = jiffies;//记录下时间
netif_wake_queue(dev);//重新启动队列
writeb(reg_save, db->io_addr);
spin_unlock_irqrestore(&db->lock, flags);
}
static void dm9000_send_packet(struct net_device *dev,
int ip_summed,
u16 pkt_len)//发送函数
{
board_info_t *dm = to_dm9000_board(dev);
/* The DM9000 is not smart enough to leave fragmented packets alone. */
if (dm->ip_summed != ip_summed) {
if (ip_summed == CHECKSUM_NONE)
iow(dm, DM9000_TCCR, 0);
else
iow(dm, DM9000_TCCR, TCCR_IP | TCCR_UDP | TCCR_TCP);
dm->ip_summed = ip_summed;
}
iow(dm, DM9000_TXPLL, pkt_len);
iow(dm, DM9000_TXPLH, pkt_len >> 8);//写SRAM长度到TX Packet Length Register
//开启芯片发送,即TX Request位
iow(dm, DM9000_TCR, TCR_TXREQ); /* Cleared after TX complete */
}
//硬件启动数据包发送,由于该网卡一次配置两个数据包故在第一个数据包发送完成之前调用两次
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__);
if (db->tx_pkt_cnt > 1)//发送队列已启动
return NETDEV_TX_BUSY;
spin_lock_irqsave(&db->lock, flags);
writeb(DM9000_MWCMD, db->io_addr);
(db->outblk)(db->io_data, skb->data, skb->len);//将套接字缓冲数据复制到SRAM
dev->stats.tx_bytes += skb->len;
db->tx_pkt_cnt++;//发送值加1
//因为该网卡共有2个包;第一个包立即发送,第二个包加入到队列中
if (db->tx_pkt_cnt == 1) {
dm9000_send_packet(dev, skb->ip_summed, skb->len);//第一个包发送
} else {//第二次调用时,保存长度和IP校验和
db->queue_pkt_len = skb->len;
db->queue_ip_summed = skb->ip_summed;
netif_stop_queue(dev);//停止队列
}
spin_unlock_irqrestore(&db->lock, flags);
dev_kfree_skb(skb);//释放套接字缓冲
return NETDEV_TX_OK;
}
//第一个数据包完成后,继续发送第二个数据包
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)) {//为1和2数据包从上层提交到链路层吗?
db->tx_pkt_cnt--;//发送了一个包其数量减少1
dev->stats.tx_packets++;
if (netif_msg_tx_done(db))//输出成功发送调试信息
dev_dbg(db->dev, "tx done, NSR %02x/n", tx_status);
if (db->tx_pkt_cnt > 0)//发送第二个包
dm9000_send_packet(dev, db->queue_ip_summed,
db->queue_pkt_len);
netif_wake_queue(dev);//重新启动队列
}
}
struct dm9000_rxhdr {//接收数据包头结构4字节
u8 RxPktReady;//01h
u8 RxStatus;//status
__le16 RxLen;//BYTE_COUNT low BYTE_COUNT high
} __attribute__((__packed__));
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;//保存读取数据 在SRAM中地址值
bool GoodPacket;//数据包标志位
int RxLen;
/* Check packet ready or not */
do {
//ior(db, DM9000_MRCMDX);
//rxbyte = readb(db->io_data);
rxbyte = ior(db, DM9000_MRCMDX);//读接收数据(设置IMR_PAR,读取SRAM地址从0C00开始)
if (rxbyte & DM9000_PKT_ERR) {//数据包头第一个字节为1,为0则错
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))//非1也错
return;
GoodPacket = true;
writeb(DM9000_MRCMD, db->io_addr);
(db->inblk)(db->io_data, &rxhdr, sizeof(rxhdr));//读取4字节保存到rxhdr
RxLen = le16_to_cpu(rxhdr.RxLen);//或RxLen = rxhdr.RxLen
if (netif_msg_rx_status(db))//读完成信息
dev_dbg(db->dev, "RX: status %02x, length %04x/n",
rxhdr.RxStatus, RxLen);
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) {//数据小于1536
dev_dbg(db->dev, "RST: RX Len:%x/n", RxLen);
}
//rxhdr.RxStatus与读状态寄存器数据一致否?有一个位一致表明发生错误
if (rxhdr.RxStatus & (RSR_FOE | RSR_CE | RSR_AE |
RSR_PLE | RSR_RWTO |
RSR_LCS | RSR_RF)) {
GoodPacket = false;
if (rxhdr.RxStatus & RSR_FOE) {//FIFO Overflow Error
if (netif_msg_rx_err(db))
dev_dbg(db->dev, "fifo error/n");
dev->stats.rx_fifo_errors++;//状态计数
}
if (rxhdr.RxStatus & RSR_CE) {//CRC Error
if (netif_msg_rx_err(db))
dev_dbg(db->dev, "crc error/n");
dev->stats.rx_crc_errors++;//状态计数
}
if (rxhdr.RxStatus & RSR_RF) {//the received frame is smaller than 64 bytes
if (netif_msg_rx_err(db))
dev_dbg(db->dev, "length error/n");
dev->stats.rx_length_errors++;//状态计数
}
}
//数据包正常时分配套接字和长度为RxLen + 4的数据缓冲区
if (GoodPacket&& ((skb = dev_alloc_skb(RxLen + 4)) != NULL)) {
skb_reserve(skb, 2);//ip头为16字节对齐,而以太网头为14B需要2B.
rdptr = (u8 *) skb_put(skb, RxLen - 4);//去掉FCS的4字节,rdptr=data=head+2
(db->inblk)(db->io_data, rdptr, RxLen);//虽然FCS无用但是还是复制到缓冲区,只是不提交给上层网络
dev->stats.rx_bytes += RxLen;//更新状态
skb->protocol = eth_type_trans(skb, dev);//获取上层(即网络层协议一般即IP协议)协议
if (db->rx_csum) {
if ((((rxbyte & 0x1c) << 3) & rxbyte) == 0)
skb->ip_summed = CHECKSUM_UNNECESSARY;//不要做任何校验
else
skb->ip_summed = CHECKSUM_NONE;//校验和还没被验证,由系统软件来完成这个任务.
}
netif_rx(skb);//将数据链路头去掉后从链路层提交到网络层
dev->stats.rx_packets++;//更新状态
} else {//数据出错
(db->dumpblk)(db->io_data, RxLen);
}
} while (rxbyte & DM9000_PKT_RDY);
}
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 */
spin_lock_irqsave(&db->lock, flags);
/* Save previous register address */
reg_save = readb(db->io_addr);
iow(db, DM9000_IMR, IMR_PAR);//关闭网卡读写中断IMR_PAR设置SRAM地址特性
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);
if (int_status & ISR_PRS)//读取中断否?
dm9000_rx(dev);
if (int_status & ISR_PTS)//传送中断否?
dm9000_tx_done(dev, db);
if (db->type != TYPE_DM9000E) {
if (int_status & ISR_LNKCHNG) {//为A B类型时还需判断是否为连接状态改变引发中断
schedule_delayed_work(&db->phy_poll, 1);//调度定时工作队列以通知内核连接情况
}
}
iow(db, DM9000_IMR, db->imr_all);//使能读写中断
writeb(reg_save, db->io_addr);/* Restore previous register address */
spin_unlock_irqrestore(&db->lock, flags);
return IRQ_HANDLED;
}
#ifdef CONFIG_NET_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
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 (irqflags == IRQF_TRIGGER_NONE)//无中断设置
dev_warn(db->dev, "WARNING: no IRQ resource flags set./n");
irqflags |= IRQF_SHARED;//读写共享中断号
if (request_irq(dev->irq, &dm9000_interrupt, irqflags, dev->name, dev))//申请中断
return -EAGAIN;
dm9000_reset(db);//复位dm9000
dm9000_init_dm9000(dev);//初始化dm9000
db->dbug_cnt = 0;
mii_check_media(&db->mii, netif_msg_link(db), 1);//检测MII是否改变,没使用
netif_start_queue(dev);//启动发送队列,告诉上层网络协定这个驱动程序还有空的缓冲区可用
dm9000_schedule_poll(db);//初始化定时器来调度工作队列
return 0;
}
static void dm9000_msleep(board_info_t *db, unsigned int ms)//延时函数
{
if (db->in_suspend)
mdelay(ms);//无
else
msleep(ms);//睡眠等待
}
//操作mii寄存器是以PHY Address Register和PHY Data Register为基础的
//其方式为先将寄存器地址写入PHY Address Register中,再从PHY Data Register
//中读或写数据后,再发送读写命令即可
static int
dm9000_phy_read(struct net_device *dev, int phy_reg_unused, int reg)//读mii寄存器
{
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);
reg_save = readb(db->io_addr);
iow(db, DM9000_EPAR, DM9000_PHY | reg);//写入MII寄存器地址到EPAR
iow(db, DM9000_EPCR, EPCR_ERPRR | EPCR_EPOS);//EPCR中设置选择PHY且发送读命令
writeb(reg_save, db->io_addr);
spin_unlock_irqrestore(&db->lock,flags);
dm9000_msleep(db, 1);//等待操作完成
spin_lock_irqsave(&db->lock,flags);
reg_save = readb(db->io_addr);
iow(db, DM9000_EPCR, 0x0);//EPCR使用后需清除
ret = (ior(db, DM9000_EPDRH) << 8) | ior(db, DM9000_EPDRL);//控制值读出EPDR
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;
}
//写mii寄存器
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);
reg_save = readb(db->io_addr);//保存数据IO接口地址
iow(db, DM9000_EPAR, DM9000_PHY | reg);//写入MII寄存器地址到EPAR
iow(db, DM9000_EPDRL, value);
iow(db, DM9000_EPDRH, value >> 8);//控制值写入EPDR
iow(db, DM9000_EPCR, EPCR_EPOS | EPCR_ERPRW);//EPCR中设置选择PHY且发送写命令
writeb(reg_save, db->io_addr);//恢复数据
spin_unlock_irqrestore(&db->lock, flags);
dm9000_msleep(db, 1); //等待操作完成
spin_lock_irqsave(&db->lock,flags);
reg_save = readb(db->io_addr);
iow(db, DM9000_EPCR, 0x0); //EPCR使用后需清除
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);
dm9000_phy_write(dev, 0, MII_BMCR, BMCR_RESET); //phy复位
iow(db, DM9000_GPR, 0x01); /* Power-Down PHY */
iow(db, DM9000_IMR, IMR_PAR); /* Disable all interrupt */
iow(db, DM9000_RCR, 0x00); /* Disable RX */
}
//停止设备
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_irq(ndev->irq, ndev);//注销中断
dm9000_shutdown(ndev);//关闭设备
return 0;
}
static const struct net_device_ops dm9000_netdev_ops = {//设备操作集
.ndo_open = dm9000_open,
.ndo_stop = dm9000_stop,
.ndo_start_xmit = dm9000_start_xmit,//启动发送
.ndo_tx_timeout = dm9000_timeout,
.ndo_set_multicast_list = dm9000_hash_table,//设置组播地址表
.ndo_do_ioctl = dm9000_ioctl,//IO命令控制
.ndo_change_mtu = eth_change_mtu,//改变最大传输值(初始化时已赋值)
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,//设置设备的MAC地址(初始化时已赋值)
#ifdef CONFIG_NET_POLL_CONTROLLER//轮询控制
.ndo_poll_controller = dm9000_poll_controller,
#endif
};
static int __devinit
dm9000_probe(struct platform_device *pdev)
{
struct dm9000_plat_data *pdata = pdev->dev.platform_data;//私有数据
struct board_info *db;
struct net_device *ndev;
const unsigned char *mac_src;
int ret = 0;
int iosize;
int i;
u32 id_val;
/* Init network device */
ndev = alloc_etherdev(sizeof(struct board_info));//分配net_device结构,初始化公共成员
if (!ndev) {
dev_err(&pdev->dev, "could not allocate device./n");
return -ENOMEM;
}
SET_NETDEV_DEV(ndev, &pdev->dev);
//为网络逻辑设备设置一个sysfs参照物理设备,若设置在注册之前则初始化时建立连接
dev_dbg(&pdev->dev, "dm9000_probe()/n");
db = netdev_priv(ndev);//指向net_device的priv
db->dev = &pdev->dev;//父设备
db->ndev = ndev;
spin_lock_init(&db->lock);//初始化锁
mutex_init(&db->addr_lock);//初始化地址锁
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 = resource_size(db->addr_res);
db->addr_req = request_mem_region(db->addr_res->start, iosize,
pdev->name);//为寄存器地址申请IO内存
if (db->addr_req == NULL) {//失败
dev_err(db->dev, "cannot claim address reg area/n");
ret = -EIO;
goto out;
}
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 = resource_size(db->data_res);
db->data_req = request_mem_region(db->data_res->start, iosize,
pdev->name);//为数据IO地址申请IO内存
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;
}
ndev->base_addr = (unsigned long)db->io_addr;//初始化设备IO地址
ndev->irq = db->irq_res->start;//初始化设备中断号
dm9000_set_io(db, iosize);//没有定义私有数据作为缺省值
if (pdata != NULL) {
if (pdata->flags & DM9000_PLATF_8BITONLY)
dm9000_set_io(db, 1);
if (pdata->flags & DM9000_PLATF_16BITONLY)//使用16位宽度
dm9000_set_io(db, 2);
if (pdata->flags & DM9000_PLATF_32BITONLY)
dm9000_set_io(db, 4);
//若没有赋值使用缺省函数
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;//DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM
}
#ifdef CONFIG_DM9000_FORCE_SIMPLE_PHY_POLL//使用片内PHY判断连接状态
db->flags |= DM9000_PLATF_SIMPLE_PHY;//使用片内PHY
#endif
dm9000_reset(db);//复位后选择eeprom
for (i = 0; i < 8; i++) {//读取多次,有时读错
id_val = ior(db, DM9000_VIDL);
id_val |= (u32)ior(db, DM9000_VIDH) << 8;//读取Vendor ID
id_val |= (u32)ior(db, DM9000_PIDL) << 16;
id_val |= (u32)ior(db, DM9000_PIDH) << 24;//读取Product ID
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;
}
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;//a型
break;
case CHIPR_DM9000B:
db->type = TYPE_DM9000B;//B型
break;
default:
dev_dbg(db->dev, "ID %02x => defaulting to DM9000E/n", id_val);
db->type = TYPE_DM9000E;
}
/* dm9000a/b 具有硬件检验和的能力 */
if (db->type == TYPE_DM9000A || db->type == TYPE_DM9000B) {
db->can_csum = 1;
db->rx_csum = 1;
ndev->features |= NETIF_F_IP_CSUM;//设备传输层使能校验和
}
//一下为初始化网络设备数据结构的私有成员
ether_setup(ndev);//调用alloc_etherdev分配给结构的初始化函数
ndev->netdev_ops = &dm9000_netdev_ops;//网络设备操作集
ndev->watchdog_timeo = msecs_to_jiffies(watchdog);//看门狗溢出时间
ndev->ethtool_ops = &dm9000_ethtool_ops;//Ethtool框架
db->msg_enable = NETIF_MSG_LINK;//连接消息
//一下为使用片外PHY时MII接口芯片的信息
db->mii.phy_id_mask = 0x1f;//PHY ID掩码
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;//mii接口读取函数
db->mii.mdio_write = dm9000_phy_write;//mii接口写函数
mac_src = "eeprom";//MAC码保存器件名称为eeprom
for (i = 0; i < 6; i += 2)
dm9000_read_eeprom(db, i / 2, ndev->dev_addr+i);
//读取MAC到dev_addr数组,没有EEPROM为组播地址FF:FF:FF:FF:FF:FF
//FF:FF:FF:FF:FF:FF组播地址
if (!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) {//非有效以太网地址时
mac_src = "platform data";//MAC码保存器件名称为平台设备
memcpy(ndev->dev_addr, pdata->dev_addr, 6);//我们没有设置pdata->dev_addr
}
if (!is_valid_ether_addr(ndev->dev_addr)) {//非有效以太网地址时
mac_src = "chip";//MAC码保存器件名称为网络设备
for (i = 0; i < 6; i++)
ndev->dev_addr[i] = ior(db, i+DM9000_PAR);
//读取Physical Address Register ( 10H~15H )
}
//物理地址可以自己通过pdata->dev_addr设定或是直接读取芯片中的物理地址
if (!is_valid_ether_addr(ndev->dev_addr))
dev_warn(db->dev, "%s: Invalid ethernet MAC address. Please "
"set using ifconfig/n", ndev->name);
//在最外层网络设备以平台设备注册,所以将驱动数据与平台设备关联起来
platform_set_drvdata(pdev, ndev);
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:
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 device *dev)//挂起函数
{
struct platform_device *pdev = to_platform_device(dev);
struct net_device *ndev = platform_get_drvdata(pdev);
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 device *dev)//恢复函数
{
struct platform_device *pdev = to_platform_device(dev);
struct net_device *ndev = platform_get_drvdata(pdev);
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 struct dev_pm_ops dm9000_drv_pm_ops = {
.suspend = dm9000_drv_suspend,
.resume = dm9000_drv_resume,
};
static int __devexit //__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);//释放数据结构
dev_dbg(&pdev->dev, "released and freed device/n");
return 0;
}
static struct platform_driver dm9000_driver = {//驱动
.driver = {
.name = "dm9000",
.owner = THIS_MODULE,
.pm = &dm9000_drv_pm_ops,//电源操作函数
},
.probe = dm9000_probe,//探测函数
.remove = __devexit_p(dm9000_drv_remove),
//移除函数_devexit_p 用于初始化由__devexit标记的函数的指针
};
static int __init
dm9000_init(void)
{
printk(KERN_INFO "%s Ethernet Driver, V%s/n", CARDNAME, DRV_VERSION);
return platform_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");