BSP-NetworkCard

    技术2022-05-20  34

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

    1.        RAM分布

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

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

     

    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)