上篇文章已经基本完成网卡驱动的基本框架了, 从这篇就要开始涉及到DM9000的硬件了。 为了开发和调试便利, 我建议大家开始写接下来的代码前先阅读下DM9000的datasheet, 并在与开发板直连的电脑上装一个wireshark。
DM9000的datasheet和当前的完整代码我已经上传到我的资源里了, 供需要的朋友下载。http://download.csdn.net/detail/zlyong0018/5620723
同时我建议之前没接触网卡方面的朋友可以看一下赵老师关于DM9000裸奔的博客。http://blog.csdn.net/zhaocj/article/details/5672588
好了, 现在我们继续DM9000的驱动。
首先我们先定义DM9000内部寄存器相关的宏, 这部分大家没必要自己码, 可以直接copy内核源码dm9000.h
// **************** register ********************
#define DM9000_NCR 0x00
#define DM9000_NSR 0x01
#define DM9000_TCR 0x02
#define DM9000_TSR1 0x03
#define DM9000_TSR2 0x04
#define DM9000_RCR 0x05
#define DM9000_RSR 0x06
#define DM9000_ROCR 0x07
#define DM9000_BPTR 0x08
#define DM9000_FCTR 0x09
#define DM9000_FCR 0x0A
#define DM9000_EPCR 0x0B
#define DM9000_EPAR 0x0C
#define DM9000_EPDRL 0x0D
#define DM9000_EPDRH 0x0E
#define DM9000_WCR 0x0F
#define DM9000_PAR 0x10
#define DM9000_PAB0 0x10
#define DM9000_PAB1 0x11
#define DM9000_PAB2 0x12
#define DM9000_PAB3 0x13
#define DM9000_PAB4 0x14
#define DM9000_PAB5 0x15
#define DM9000_MAR 0x16
#define DM9000_GPCR 0x1e
#define DM9000_GPR 0x1f
#define DM9000_TRPAL 0x22
#define DM9000_TRPAH 0x23
#define DM9000_RWPAL 0x24
#define DM9000_RWPAH 0x25
#define DM9000_VIDL 0x28
#define DM9000_VIDH 0x29
#define DM9000_PIDL 0x2A
#define DM9000_PIDH 0x2B
#define DM9000_CHIPR 0x2C
#define DM9000_SMCR 0x2F
#define DM9000_ETXCSR 0x30
#define DM9000_TCCR 0x31
#define DM9000_RCSR 0x32
#define CHIPR_DM9000A 0x19
#define CHIPR_DM9000B 0x1A
#define DM9000_MRCMDX 0xF0
#define DM9000_MRCMD 0xF2
#define DM9000_MRRL 0xF4
#define DM9000_MRRH 0xF5
#define DM9000_MWCMDX 0xF6
#define DM9000_MWCMD 0xF8
#define DM9000_MWRL 0xFA
#define DM9000_MWRH 0xFB
#define DM9000_TXPLL 0xFC
#define DM9000_TXPLH 0xFD
#define DM9000_ISR 0xFE
#define DM9000_IMR 0xFF
#define NCR_EXT_PHY (1<<7)
#define NCR_WAKEEN (1<<6)
#define NCR_FCOL (1<<4)
#define NCR_FDX (1<<3)
#define NCR_LBK (3<<1)
#define NCR_RST (1<<0)
#define NSR_SPEED (1<<7)
#define NSR_LINKST (1<<6)
#define NSR_WAKEST (1<<5)
#define NSR_TX2END (1<<3)
#define NSR_TX1END (1<<2)
#define NSR_RXOV (1<<1)
#define TCR_TJDIS (1<<6)
#define TCR_EXCECM (1<<5)
#define TCR_PAD_DIS2 (1<<4)
#define TCR_CRC_DIS2 (1<<3)
#define TCR_PAD_DIS1 (1<<2)
#define TCR_CRC_DIS1 (1<<1)
#define TCR_TXREQ (1<<0)
#define TSR_TJTO (1<<7)
#define TSR_LC (1<<6)
#define TSR_NC (1<<5)
#define TSR_LCOL (1<<4)
#define TSR_COL (1<<3)
#define TSR_EC (1<<2)
#define RCR_WTDIS (1<<6)
#define RCR_DIS_LONG (1<<5)
#define RCR_DIS_CRC (1<<4)
#define RCR_ALL (1<<3)
#define RCR_RUNT (1<<2)
#define RCR_PRMSC (1<<1)
#define RCR_RXEN (1<<0)
#define RSR_RF (1<<7)
#define RSR_MF (1<<6)
#define RSR_LCS (1<<5)
#define RSR_RWTO (1<<4)
#define RSR_PLE (1<<3)
#define RSR_AE (1<<2)
#define RSR_CE (1<<1)
#define RSR_FOE (1<<0)
#define WCR_LINKEN (1 << 5)
#define WCR_SAMPLEEN (1 << 4)
#define WCR_MAGICEN (1 << 3)
#define WCR_LINKST (1 << 2)
#define WCR_SAMPLEST (1 << 1)
#define WCR_MAGICST (1 << 0)
#define FCTR_HWOT(ot) (( ot & 0xf ) << 4 )
#define FCTR_LWOT(ot) ( ot & 0xf )
#define IMR_PAR (1<<7)
#define IMR_ROOM (1<<3)
#define IMR_ROM (1<<2)
#define IMR_PTM (1<<1)
#define IMR_PRM (1<<0)
#define ISR_ROOS (1<<3)
#define ISR_ROS (1<<2)
#define ISR_PTS (1<<1)
#define ISR_PRS (1<<0)
#define ISR_CLR_STATUS (ISR_ROOS | ISR_ROS | ISR_PTS | ISR_PRS)
#define EPCR_REEP (1<<5)
#define EPCR_WEP (1<<4)
#define EPCR_EPOS (1<<3)
#define EPCR_ERPRR (1<<2)
#define EPCR_ERPRW (1<<1)
#define EPCR_ERRE (1<<0)
#define GPCR_GEP_CNTL (1<<0)
#define TCCR_IP (1<<0)
#define TCCR_TCP (1<<1)
#define TCCR_UDP (1<<2)
#define RCSR_UDP_BAD (1<<7)
#define RCSR_TCP_BAD (1<<6)
#define RCSR_IP_BAD (1<<5)
#define RCSR_UDP (1<<4)
#define RCSR_TCP (1<<3)
#define RCSR_IP (1<<2)
#define RCSR_CSUM (1<<1)
#define RCSR_DISCARD (1<<0)
#define DM9000_PKT_RDY 0x01 /* Packet ready to receive */
#define DM9000_PKT_ERR 0x02
#define DM9000_PKT_MAX 1536 /* Received packet max size */
/* DM9000A / DM9000B definitions */
#define IMR_LNKCHNG (1<<5)
#define IMR_UNDERRUN (1<<4)
#define ISR_LNKCHNG (1<<5)
#define ISR_UNDERRUN (1<<4)
定义好DM9000内部寄存器的宏后, 我们分析下DM9000在mini2440里的资源。
从mini2440的芯片手册上可以看到,
1. DM9000的AEN引脚与2440的nGCS4相连, 故DM9000的基地址为0x20000000
2. CMD引脚与LADDR2相连, 且CMD为低电平时数据总线传输内部寄存器地址, CMD引脚为高电平时数据总线传输的为数据, 故只要地址第2位(zerobase)
为0, 数据线上传输的就是地址, 第2位为1时数据线上传输的就是数据。 为了让大家看出效果, 我们把DM9000的io_reg地址设置为0x20000300, io_data地址
设置为0x20000204
3. INT引脚与EINT7相连
static struct resource lydm9k_resource[] = {
[0] = {
.start = 0x20000300,
.end = 0x20000303,
.flags = IORESOURCE_MEM
},
[1] = {
.start = 0x20000204,
.end = 0x20000207,
.flags = IORESOURCE_MEM
},
[2] = {
.start = IRQ_EINT7,
.end = IRQ_EINT7,
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE
}
};
struct lydm9k_priv添加如下成员, 记录DM9000外设IO相关的信息, IRQ资源, MAC号, 及外设IO的保护锁
struct lydm9k_priv {
struct platform_device *pdev;
struct resource *addr_res;
struct resource *data_res;
struct resource *irq_res;
void __iomem *io_reg;
void __iomem *io_data;
unsigned char mac[6];
spinlock_t spin_reg;
};
OK, 现在开始在probe函数里面添加IO内存申请,IO内存重映射,初始化自旋锁, 并填充struct lydm9k_priv中新添加的字段(sorry, 代码是在linux上写的, 而这篇文章是在windows写的, 注释出现乱码了, 这部分代码比较简单, 就将就看吧)
static int __devinit lydm9k_probe(struct platform_device *pdev)
{
int ret;
struct net_device *ndev;
struct lydm9k_priv *priv;
struct lydm9k_plat_data *pdata = pdev->dev.platform_data;
size_t io_size;
struct resource *addr_res;
struct resource *data_res;
printdbg("detect a dm9k device\n");
// 鐢宠ndev绌洪棿
ndev = alloc_etherdev(sizeof(struct lydm9k_priv));
if (NULL == ndev)
{
dm9kmsg("fail to alloc_etherdev\n");
return -ENOMEM;
}
// 璁剧疆ndev鐨勭埗璁惧涓簆dev
SET_NETDEV_DEV(ndev, &pdev->dev);
// 璁剧疆ndev鍜宲dev鐩存帴鐨勫叧鑱?
platform_set_drvdata(pdev, ndev);
priv = netdev_priv(ndev);
priv->pdev = pdev;
// remap
addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
priv->irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if( (NULL == addr_res)
|| (NULL == data_res)
|| (NULL == priv->irq_res))
{
dm9kmsg("cannot get platform resource\n");
ret = -ENOENT;
goto failure_platform_get_resource;
}
io_size = resource_size(addr_res);
if(NULL == request_mem_region( addr_res->start,
io_size,
pdev->name))
{
dm9kmsg("cannot claim addr reg area");
ret = -EIO;
goto failure_request_mem_region_addr;
}
priv->io_reg = ioremap(addr_res->start, io_size);
if(NULL == priv->io_reg)
{
dm9kmsg("fail to remap io addr\n");
ret = -EINVAL;
goto failure_ioremap_addr;
}
io_size = resource_size(data_res);
if(NULL == request_mem_region( data_res->start,
io_size,
pdev->name))
{
dm9kmsg("cannot claim data reg area");
ret = -EIO;
goto failure_request_mem_region_data;
}
priv->io_data = ioremap(data_res->start, io_size);
if(NULL == priv->io_data)
{
dm9kmsg("fail to remap io data\n");
ret = -EINVAL;
goto failure_ioremap_data;
}
ndev->irq = priv->irq_res->start;
priv->addr_res = addr_res;
priv->data_res = data_res;
// 鍒濆鍖栬嚜鏃嬮攣
spin_lock_init(&priv->spin_reg);
// 璁剧疆鎿嶄綔鍑芥暟闆?mac/watchdog_timeo
ndev->netdev_ops = &lydm9k_ops;
ndev->watchdog_timeo =
msecs_to_jiffies(pdata->watchdog_timeo_msecs);
memcpy(ndev->dev_addr, pdata->mac, 6);
memcpy(priv->mac, pdata->mac, 6);
// 娉ㄥ唽缃戝崱
ret = register_netdev(ndev);
if(0 > ret)
{
dm9kmsg("fail to register netdev\n");
goto failure_register_netdev;
}
return 0;
failure_register_netdev:
iounmap(priv->io_data);
failure_ioremap_data:
io_size = resource_size(data_res);
release_mem_region(data_res->start, io_size);
failure_request_mem_region_data:
iounmap(priv->io_reg);
failure_ioremap_addr:
io_size = resource_size(addr_res);
release_mem_region(addr_res->start, io_size);
failure_request_mem_region_addr:
do {} while(0);
failure_platform_get_resource:
platform_set_drvdata(pdev, NULL);
free_netdev(ndev);
return ret;
}
有资源的申请, 就要有释放
static int __devexit lydm9k_remove(struct platform_device *pdev)
{
struct net_device *ndev = platform_get_drvdata(pdev);
struct lydm9k_priv *priv = netdev_priv(ndev);
size_t io_size;
unregister_netdev(ndev);
iounmap(priv->io_data);
io_size = resource_size(priv->data_res);
release_mem_region(priv->data_res->start, io_size);
iounmap(priv->io_reg);
io_size = resource_size(priv->addr_res);
release_mem_region(priv->addr_res->start, io_size);
platform_set_drvdata(pdev, NULL);
free_netdev(ndev);
printdbg("remove ok\n");
return 0;
}
IO内存申请映射好了, 看看我们接下来要要做什么:
1. 首先我们要初始化网卡, 能使网卡能按你所想要的那样工作;
2. 然后我们要注册一个中断, 当网卡接收到数据的时候相应的中断服务例程就将网卡中的数据取出来发给上层
没错, 接下来我们要在open函数中实现网卡的初始化, 并注册网卡的中断服务例程。 大家应该知道, 网卡的初始化和数据存取需要频繁地操作网卡的寄存器故在实现open函数前, 我们先写几个网卡寄存器操作的小函数, 提高代码的复用率
为了避免在往网卡写地址和读写数据中间发生中断, 以致中断服务例程修改了网卡的操作地址使得后面的读写位置错误, 我们将读写操作保护在锁里并禁止中断
// 写寄存器
static void inline regw(struct lydm9k_priv *priv,
unsigned char reg,
unsigned char data)
{
unsigned long irq_tmp;
spin_lock_irqsave(&priv->spin_reg, irq_tmp);
writeb(reg, priv->io_reg);
writeb(data, priv->io_data);
spin_unlock_irqrestore(&priv->spin_reg, irq_tmp);
}
// 读寄存器
static unsigned char inline regr(struct lydm9k_priv *priv,
unsigned char reg)
{
unsigned char val;
unsigned long irq_tmp;
spin_lock_irqsave(&priv->spin_reg, irq_tmp);
writeb(reg, priv->io_reg);
val = readb(priv->io_data);
spin_unlock_irqrestore(&priv->spin_reg, irq_tmp);
return val;
}
// 连续写寄存器
static void outblk(struct lydm9k_priv *priv,
unsigned char reg, void *data, int count)
{
unsigned long irq_tmp;
spin_lock_irqsave(&priv->spin_reg, irq_tmp);
writeb(reg, priv->io_reg);
writesw(priv->io_data, data, (count+1) >> 1);
spin_unlock_irqrestore(&priv->spin_reg, irq_tmp);
}
// 连续读寄存器
static void inblk(struct lydm9k_priv *priv,
unsigned char reg, void *data, int count)
{
unsigned long irq_tmp;
spin_lock_irqsave(&priv->spin_reg, irq_tmp);
writeb(reg, priv->io_reg);
readsw(priv->io_data, data, (count+1) >> 1);
spin_unlock_irqrestore(&priv->spin_reg, irq_tmp);
}
// 丢弃接收缓冲区
static void dumpblk(struct lydm9k_priv *priv,
unsigned char reg, int count)
{
unsigned long irq_tmp;
spin_lock_irqsave(&priv->spin_reg, irq_tmp);
writeb(reg, priv->io_reg);
while(count > 0)
{
readw(priv->io_data);
count -= 2;
}
spin_unlock_irqrestore(&priv->spin_reg, irq_tmp);
}
OK, 现在万事俱备,只欠东风了, 明天我们就开始实现open函数。