Web 51 - RTL8019AS Packet Driver

RTL8019AS Packet Driver 2  software.html  RTL8019AS Packet Driver 4

Part 3 - Receiving packets

Receiving of packets is the most complicated part of the driver. First, let's show the code that reads packet header and status information.
;*************************************************************
;***            R E C E I V E   P A C K E T                 **
;*************************************************************

;*************************************************************
;** read header of receive packet
;**
ReadPacketHeader:
        mov     R2,#high(SIZE_OF_8019_HDR+SIZE_OF_ETH_PKT_HDR)
        mov     R3,#low(SIZE_OF_8019_HDR+SIZE_OF_ETH_PKT_HDR) ;R2R3 Byte Count
;       MOV     R4,#...         ;R4R5 Remote Start Address
        MOV     R5,#0
        MOV     R1,#rcv_hdr     ;R1 store pointer
        MOV     DPTR,#icall_save;pointer to procedure BYTE(@R1) save
        AJMP    DMARead

The header, as read from RTL, looks like this:

rcv_hdr Received frame status 1 byte
rcv_hdr_nxt_pg Page after this frame 1 byte
rcv_hdr_size Length of this frame 2 bytes
eth_pkt_hdr_dest Destination Address 6 bytes
eth_pkt_hdr_src Source Address 6 bytes
eth_pkt_hdr_type Length 2 bytes

Processing of received packet follows.

;*************************************************************
;** receive packet
;**
rcv_pkt:

First, make sure there's something to process.

        ISAIN   EN0_ISR         ; Get pending interrupts
        JNZ     ISRtestOverrun
        RET                     ; Ret if none

Test for buffer overrun that would damage data.

ISRtestOverrun:
                                                ; Was there an overrun ?
        JB      BITISR_OVER,recv_overrun        ; Go if so
        AJMP    recv_no_overrun                 ; Go if not

Bufer overrun, deal with it.

;*************************************************************
;** receive overrun
;**

First, save device status.

recv_overrun:
        ISAIN   EN_CMD          ; read Chip's command register
        PUSH    Acc             ; and save it

Stop the device.

;; Stop the NIC
        ISAOUT  EN_CMD, #EN_PAGE0+EN_NODMA+EN_STOP
                                ; Remote DMA, Stop and reset the chip

Wait for packet receiving or transmission to finish.

;; Wait 1.6ms for the NIC to stop transmitting or receiving a packet. National
;; says monitoring the ISR RST bit is not reliable, so a wait of the maximum
;; packet time (1.2ms) plus some padding is required.
        ACALL   longpause

Begin "revival" of the device, setting transmission length to zero.

;; Reset RBCR[01] back to zero as per magic incantation.
        BEGINPORTDATA
        PORTDATA        EN0_RCNTHI,0    ; MSB Remote byte count reg
        PORTDATA        EN0_RCNTLO,0    ; LSB Remote byte count reg
        ENDPORTDATA

Test whether a transmission was stopped. If so, check if it finished correctly; if it did not, make a note of that and retry transmission later.

;; check the saved state of the EN_TRANS bit in the command register
        CLR     flagResend
        POP     Acc             ; pop Chip's command register
        JNB     BIT_TRANS,rcv_ovr_loopback
;; Transmitter was running, see if it finished or died
        ISAIN   EN0_ISR         ; Get pending interrupts
        ANL     A,#ENISR_TX_ERR+ENISR_TX ; Did the transmitter finish?
        JNZ     rcv_ovr_loopback        ; one will be set if TX finished
; Transmitter did not complete, remember to resend the packet later.
        SETB    flagResend
rcv_ovr_loopback:

Until the rescue operation finishes, device needs to be in loopback mode to avoid receiving data from the line. After that it can be restarted.

;; Have to enter loopback mode and then restart the NIC before you are
;; allowed to slurp packets up off the ring
        BEGINPORTDATA
        PORTDATA        EN0_TXCR,ENTXCR_LOOP ; set internal loopback mode
        PORTDATA        EN_CMD, EN_PAGE0+EN_NODMA+EN_START
                                ; Start the chip running again

Check for any packets in the buffer. Do this by comparing address where the device stores data with the address where the program expects the next packet to be.

;; Verify that there is really a packet to receive by fetching the current
;; page pointer and comparing it to the next packet pointer.
        PORTDATA        EN_CMD, EN_PAGE1+EN_NODMA       ; Set Page 1
        ENDPORTDATA
        ISAIN   EN1_CURPAG      ; Current memory page
        MOV     R4,A
        ISAOUT  EN_CMD, #EN_PAGE0+EN_NODMA      ; Page 1, Remote DMA
        MOV     A,next_packet
        XRL     A,R4            ; Check if buffer empty
        JNZ     rcv_ovr_rx_one  ; O.K. get the NIC header

No packet in memory, skip verification.

; NO PACKET IN THE RING AFTER AN OVW INTERRUPT??? Can this ever happen?
; YES! if overrun happend between a receive interrupt and the when the
; current page register is read at the start of recv_frame.
        AJMP    rcv_ovr_empty   ; Current Page == next_packet

Read header of received packet.

rcv_ovr_rx_one:
;       MOV     R5,#0           ; R4R5 Remote Start Address Register
        PUSH    AR4
        ACALL   ReadPacketHeader
        POP     AR4

Start checking the packet in the buffer for damage.

        MOV     A,rcv_hdr       ; Get the buffer status byte
        ;Received a good packet ?

First, check the "received OK" bit in its header.

        JNB     BITRSR_RXOK, rcv_ovr_ng  ; No, received debris

If OK, check if the pointer to the next packet in buffer is within allowed range.

; EVEN if the NIC header status is OK, I have seen garbaged NIC headers, so
; it doesn't hurt to range check the next packet pointer here.
        MOV     A,rcv_hdr_nxt_pg        ; pointer to next packet
        CJNE    A,#RX_START_PG,$+3      ;; First page of RX Ring
        JC      rcv_ovr_ng      ;; < RX_START_PG
                                ;;invalid pointer, Bellow around the bottom
        CJNE    A,#NE_STOP_PG,$+3;;Last page + 1 of RX Ring
        JNC     rcv_ovr_ng      ;; >= NE_STOP_PG
                                ;;invalid pointer, Above the top

Pointer is OK, store it and process the received packet.

        MOV     next_packet,A   ;save pointer to next packet
        MOV     curr_recv,R4    ;save pointer to current packet
        LCALL   ProcessEthPacket
        SJMP    rcv_ovr_ok

If the header says that packet was not received OK, or the pointer is incorrect, throw away all packets in memory.

rcv_ovr_ng:
;
; HAD TO ADD ERROR RECOVERY HERE. TO BLINDLY PROCEED AND ASSUME THE NEXT
; PACKET POINTR FROM THE NIC HEADER IS VALID IS INVITING DISASTER.
;
; Error recovery consists of killing and restarting the NIC. This drops all
; the packets in the ring, but thats better than winding up in the weeds!
;

; Instead copy the last known current page pointer into the next packet pointer
; which will result in skipping all the packets from the errored one to where
; the NIC was storing them when we entered this ISR, but prevents us from
; trying to follow totally bogus next packet pointers through the card RAM
; space.
        MOV     A,R4            ; Current memory page
        ;;MOV   next_packet,A

Then, acknowledge the interrupt and RETurn from this subroutine.

        AJMP    recv_frame_break1       ;save next_packet...

Received packet was processed, or no packet was received. Update boundary register.

rcv_ovr_ok:
rcv_ovr_empty:
        MOV     A,next_packet   ; Grap the next packet pointer
        DEC     A               ; Back up one page
        CJNE    A,#RX_START_PG,$+3 ;; Did it wrap?
        JNC     BoundaryOK      ; >= RX_START_PG
        MOV     A,#NE_STOP_PG-1 ;; Yes, back to end of ring
BoundaryOK:
        ISAOUTA EN0_BOUNDARY    ;Boundary Register

Delete flag of receiving buffer overwrite.

        BEGINPORTDATA
; Clear the OVW bit in the ISR register.
        PORTDATA        EN0_ISR, ENISR_OVER ;Interrupt status reg
                                ;Receiver overwrote the ring

Turn off loopback mode.

; Take the NIC out of loopback
        PORTDATA        EN0_TXCR, 0     ;Normal Operation
        ENDPORTDATA

If a transmission was stopped, retry it.

; Any incomplete transmission to resend?
        JNB     flagResend,recv_frame_break     ;No
; Yes, restart the transmission
        ISAOUT  EN_CMD, EN_TRANS+EN_NODMA+EN_START      ;Start the transmitter

Acknowledge interrupt and RETurn from this procedure.

        AJMP    recv_frame_break

No buffer overflow, check for any received errors.

recv_no_overrun:
        JNB     BITISR_COUNTERS, NoReadTallyCounters

If errors found, find out how many.

;*************************************************************
;** some errors, read Tally Counters
;**
        PUSH    Acc
        MOV     ADDRPORT,#EN0_COUNTER0  ;CNTR0..2
;CNTR0 Frame Alignment Error Tally Counter Register
;CNTR1 CRC Error Tally Counter Register
;CNTR2 Missed Packet Tally Counter Register
        ISAREAD
        INC     ADDRPORT
        ISAREAD
        INC     ADDRPORT
        ISAREAD
        ISAOUT  EN0_ISR, #ENISR_COUNTERS        ;Interrupt status reg
                                ;ReSet CNT (Error Tally Counters)
        POP     Acc
NoReadTallyCounters:

If a packet was received, process it regardless of errors.

        JB      BITISR_RX, PacketReceivedOK
        JB      BITISR_RX_ERR, PacketReceived

Otherwise, acknowledge interrupt and RETurn from this procedure.

        AJMP    recv_frame_break
;*************************************************************
;** test frame pointer
;**

Acknowledge received packet interrupt

PacketReceived:
PacketReceivedOK:
        ISAOUT  EN0_ISR, #ENISR_RX_ERR + ENISR_RX       ; Clear those request

If buffer is filled with received packets, store pointer.

;; Get the rx page (incoming packet pointer)
        ISAOUT  EN_CMD, #EN_PAGE1+EN_NODMA+EN_START
                                ; Switch to page 1 registers
        ISAIN   EN1_CURPAG      ; Get current page of rcv ring
        PUSH    Acc             ; ans save it
        ISAOUT  EN_CMD, #EN_PAGE0+EN_NODMA+EN_START
                                ; Back to page 0 registers

Process the entire buffer in a loop.

; This becomes the loop back point to read packets from the ring.
; now only loop back and read until those packets received at the time
; the current page register is read above have been read.
recv_frame:
;; Read all the frames ??
        MOV     R4,next_packet
        POP     Acc             ; Get current page of rcv ring
        CJNE    A,AR4,recv_more_frames

End of buffer reached, acknowledge interrupt and RETurn.

        SJMP    recv_frame_break

Read packet header

;; Remove one frame from the ring. Boundary is always a page behind.
recv_more_frames:
        PUSH    Acc             ; Save current page of rcv ring
        MOV     curr_recv,R4
;       MOV     R5,#0           ; R4R5 Remote Start Address Register
        ACALL   ReadPacketHeader

Test if the received packet is OK.

        MOV     A,rcv_hdr       ; Get the buffer status byte
        XRL     A,#ENRSR_RXOK   ; Good packet ?
        JZ      GoodPacket

Or, whether a broadcast/multicast was received.

        XRL     A,#(ENRSR_PHY + ENRSR_RXOK) XOR ENRSR_RXOK
                                ;Received broadcast/multicast
        JNZ     InvalidNextFramePtr
GoodPacket:

Packet looks OK, check the next packet pointer.

; EVEN if the NIC header status is OK, I have seen garbaged NIC headers, so
; it doesn't hurt to range check the next packet pointer here.
        MOV     A,rcv_hdr_nxt_pg; get pointer to next packet
        MOV     next_packet,A   ; and save it
        CJNE    A,#RX_START_PG,$+3      ;; First page of RX Ring
        JC      InvalidNextFramePtr     ; < RX_START_PG
        CJNE    A,#NE_STOP_PG,$+3       ;; Last page + 1 of RX Ring

If everything is OK, process the packet

        JC      ProcessFrame            ; next_packet < NE_STOP_PG
;

If something is wrong, update the pointer and return.

InvalidNextFramePtr:
        POP     Acc             ; Get current page of rcv ring
recv_frame_break1:
        MOV     next_packet,A
recv_frame_break:
        ISAOUT  EN0_ISR, #ENISR_RDC ;Interrupt status reg
                                ;remote dma complete
        RET

Processing of a packet(*)

;*************************************************************
;** process frame
;**
ProcessFrame:
        LCALL   ProcessEthPacket

Packet processed, remove it from the buffer.

        MOV     R4,next_packet
        DEC     R4
        CJNE    R4,#RX_START_PG,$+3     ;;First page of RX Ring
        JNC     SetBoundaryNxt  ; >= RX_START_PG
        MOV     R4,#NE_STOP_PG-1;;(Last page + 1)-1 of RX Ring
SetBoundaryNxt:
        ISAOUT  EN0_BOUNDARY,R4 ;Boundary page of ring buffer

And, process a next packet, if any.

        SJMP    recv_frame
Dil 2Packet Driver RTL8019AS, Part 4

(*) Web 51 uses a slightly modified call. Instead of calling ProcessEthPacket, packet driver sets the eth_state bits to flag to the point in the driver where it should continue, and returns to the calling subroutine.





Sponzored by LPhard Ltd. Graphics by GIMP Created by EasyPad

(c)Copyright 2000 - 2002, HW server & Radek Benedikt
Web51@HW.cz, Web51.HW.cz
RTL8019AS Packet Driver 2  Obsah  RTL8019AS Packet Driver 4