BSP-NetworkCard

Porting 8019的驱动,是比较老的isa网卡,相对来说比ide驱动要复杂一些,和ide一样有自己的RAM和控制寄存器

1.        RAM分布

RAM区为16K字节,地址为0x40000x7fffRAM按页存储,每256字节为一页,一般将RAM的前12页(即0x40000x4bff)存储区作为发送缓冲区,后52页(即0x4c000x7fff)存储区作为接收缓冲区

控制寄存器区为32字节,地址为0x00000x001f,寄存器分为4页:PAGE0PAGE1PAGE2PAGE3,由RTL8019ASCRCommand Register命令寄存器)中的PS1PS0位来决定要访问的页

bsp_8019 

2.        映射后的寄存器地址

经过cs片选之后,映射的8019RAM和控制寄存器有了自己的物理地址,控制寄存器的物理地址需要根据硬件连接8BITS还是16BITS来计算     

 

/*

 * This code works in 8bit mode.

 * If you need to work in 16bit mode, PLS change it!

 */

 

#include <asm/types.h>

#include <config.h>

 

 

#ifdef CONFIG_DRIVER_RTL8019_16BITS

#define ADDR_SFT 1

#else

#define ADDR_SFT 0

 

#define     RTL8019_REG_00                (RTL8019_BASE + (0x00<<ADDR_SFT))

#define     RTL8019_REG_01                (RTL8019_BASE + (0x01<<ADDR_SFT))

#define     RTL8019_REG_02                (RTL8019_BASE + (0x02<<ADDR_SFT))

#define     RTL8019_REG_03                (RTL8019_BASE + (0x03<<ADDR_SFT))

#define     RTL8019_REG_04                (RTL8019_BASE + (0x04<<ADDR_SFT))

#define     RTL8019_REG_05                (RTL8019_BASE + (0x05<<ADDR_SFT))

#define     RTL8019_REG_06                (RTL8019_BASE + (0x06<<ADDR_SFT))

#define     RTL8019_REG_07                (RTL8019_BASE + (0x07<<ADDR_SFT))

#define     RTL8019_REG_08                (RTL8019_BASE + (0x08<<ADDR_SFT))

#define     RTL8019_REG_09                (RTL8019_BASE + (0x09<<ADDR_SFT))

#define     RTL8019_REG_0a                (RTL8019_BASE + (0x0a<<ADDR_SFT))

#define     RTL8019_REG_0b                (RTL8019_BASE + (0x0b<<ADDR_SFT))

#define     RTL8019_REG_0c                (RTL8019_BASE + (0x0c<<ADDR_SFT))

#define     RTL8019_REG_0d                (RTL8019_BASE + (0x0d<<ADDR_SFT))

#define     RTL8019_REG_0e                (RTL8019_BASE + (0x0e<<ADDR_SFT))

#define     RTL8019_REG_0f                (RTL8019_BASE + (0x0f<<ADDR_SFT))

#define     RTL8019_REG_10                (RTL8019_BASE + (0x10<<ADDR_SFT))

#define     RTL8019_REG_1f                (RTL8019_BASE + (0x1f<<ADDR_SFT))

 

驱动可以用轮询或者中断来实现,比如u-boot中就是用的轮询,不知道为什么它要用轮询?实际应用程序中清一色用的是中断,毕竟效率高

3.        u-boot中的轮询驱动

轮询从网络收数据包的整个流程是:

事先CPU8019占用的端口做初始化,记得CPU初始化的时候关8019的中断,因为是轮询的;另外通过8019CS片选已经知道了RAM和控制寄存器的物理地址了

经过eth_init初始化,通过8019的控制寄存器物理地址,配置这些控制寄存器,包括中断触发方式,边沿触发或者电平触发;配置MAC地址到控制寄存器等

APP3层的一些应用的时候,会调eth_rx函数,这个函数是一个循环,读取中断状态寄存器值,轮询是否有数据到网卡

之后调用NetReceive从网卡RAM中读取数据,RTL8019_BOUNDARY是接收缓冲环读页指针,初始化=PSTARTRTL8019_CURRENT是接收缓冲环写页指针,初始化=PSTART,它们组成一个接收缓冲环;也就是说RTL8019_CURRENT8019硬件根据数据包来的时候会自动修改这个寄存器的,驱动中读取RAM中数据后,驱动会即时修改RTL8019_BOUNDARY的;具体参考注释

轮询发送相对来说简单,很好理解

/* Get a data block via Ethernet */

extern int eth_rx (void)

{

    unsigned char temp, current_point;

 

    put_reg (RTL8019_COMMAND, RTL8019_PAGE0);

 

    while (1) {

        temp = get_reg (RTL8019_INTERRUPTSTATUS);

 

        if (temp & 0x90) {

            /*overflow */

            put_reg (RTL8019_COMMAND, RTL8019_PAGE0STOP);

            udelay (2000);

            put_reg (RTL8019_REMOTEBYTECOUNT0, 0);

            put_reg (RTL8019_REMOTEBYTECOUNT1, 0);

            put_reg (RTL8019_TRANSMITCONFIGURATION, 2);

            do {

                current_point = nic_to_pc ();

            } while (get_reg (RTL8019_BOUNDARY) != current_point);

 

            put_reg (RTL8019_TRANSMITCONFIGURATION, 0xe0);

        }

 

        if (temp & 0x1) {

            /*packet received */

            do {

                put_reg (RTL8019_INTERRUPTSTATUS, 0x01);

                current_point = nic_to_pc ();

            } while (get_reg (RTL8019_BOUNDARY) != current_point);

        }

 

        if (!(temp & 0x1))

            return 0;

        /* done and exit. */

    }

}

 

static unsigned char nic_to_pc (void)

{

    unsigned char rec_head_status;

    unsigned char next_packet_pointer;

    unsigned char packet_length0;

    unsigned char packet_length1;

    unsigned short rxlen = 0;

    unsigned int i = 4;

    unsigned char current_point;

    unsigned char *addr;

 

    /*

     * The RTL8019's first 4B is packet status,page of next packet

     * and packet length(2B).So we receive the fist 4B.

     */

    put_reg (RTL8019_REMOTESTARTADDRESS1, get_reg (RTL8019_BOUNDARY));

    put_reg (RTL8019_REMOTESTARTADDRESS0, 0x00);

    /* 读取RTL8019_BOUNDARY接收缓冲环读指针,并写入起址寄存器 */

    put_reg (RTL8019_REMOTEBYTECOUNT1, 0x00);

    put_reg (RTL8019_REMOTEBYTECOUNT0, 0x04);

    /* 因为4B为包状态,所以先读4B,将0x04写入到长度寄存器 */

 

    put_reg (RTL8019_COMMAND, RTL8019_REMOTEDMARD);

    /* 执行DMA操作 */

    rec_head_status = get_reg (RTL8019_DMA_DATA);

    next_packet_pointer = get_reg (RTL8019_DMA_DATA);

    packet_length0 = get_reg (RTL8019_DMA_DATA);

    packet_length1 = get_reg (RTL8019_DMA_DATA);

    /* 读取4B的状态寄存器,包括数据长度,下一个包的起址 */

    put_reg (RTL8019_COMMAND, RTL8019_PAGE0);

    /*Packet length is in two 8bit registers */

    rxlen = packet_length1;

    rxlen = (((rxlen << 8) & 0xff00) + packet_length0);

    rxlen -= 4;

 

    if (rxlen > PKTSIZE_ALIGN + PKTALIGN)

        printf ("packet too big!/n");

 

    /*Receive the packet */

    put_reg (RTL8019_REMOTESTARTADDRESS0, 0x04);

    put_reg (RTL8019_REMOTESTARTADDRESS1, get_reg (RTL8019_BOUNDARY));

 

    put_reg (RTL8019_REMOTEBYTECOUNT0, (rxlen & 0xff));

    put_reg (RTL8019_REMOTEBYTECOUNT1, ((rxlen >> 8) & 0xff));

 

 

    put_reg (RTL8019_COMMAND, RTL8019_REMOTEDMARD);

    /* 同前的操作,前是读4B的状态,这里是读实际的包 */

    for (addr = (unsigned char *) NetRxPackets[0], i = rxlen; i > 0; i--)

        *addr++ = get_reg (RTL8019_DMA_DATA);

    /* 读取数据到*NetRxPackets指针 */

    /* Pass the packet up to the protocol layers. */

    NetReceive (NetRxPackets[0], rxlen);

 

    while (!(get_reg (RTL8019_INTERRUPTSTATUS)) & 0x40);    /* wait for the op. */

 

    /*

     * To test whether the packets are all received,get the

     * location of current point

     */

    put_reg (RTL8019_COMMAND, RTL8019_PAGE1);

    current_point = get_reg (RTL8019_CURRENT);

    /* 读取接收缓冲环写指针,返回给上层API判断用 */

    put_reg (RTL8019_COMMAND, RTL8019_PAGE0);

    put_reg (RTL8019_BOUNDARY, next_packet_pointer);

    /* 将下一个包的指针赋值给读取接收缓冲环读指针 */

    return current_point;

}

 

这是采用的Remote DMA READ方式

初始化bnry读指针 = curr写指针 = PSTART

有数据包要读时,查看bnry是否=curr,不等则说明有数据包要读

bnry初始化DMA起址寄存器0,1

4初始化DMA长度寄存器0,1

执行Remote DMA READ命令

读出网卡物理状态头(4字节),从包头中读出包长度

25读出所有数据

调整bnry指针=curr

4.        中断驱动

    中断实现方式相比轮询效率高,代码框架也类似,采用的是SEND COMMAND方式

 

5.        小结

初始化流程严格按照流程做,做一个128*64的液晶显示驱动,自以为是的少初始化一个细节,结果不管怎么样都驱动不成功

自己的网卡和CPU16位接口的,地址线有错位,CPUA1连接到网卡芯片的A0,所以编程的时候需要移位调整

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值