一步步写网卡驱动(二)

 

        上篇文章已经基本完成网卡驱动的基本框架了, 从这篇就要开始涉及到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函数。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值