Web 51 - RTL8019AS Packet Driver |
|
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 DMAReadThe 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 noneTest 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 notBufer overrun, deal with it.
;************************************************************* ;** receive overrun ;**First, save device status.
recv_overrun: ISAIN EN_CMD ; read Chip's command register PUSH Acc ; and save itStop the device.
;; Stop the NIC ISAOUT EN_CMD, #EN_PAGE0+EN_NODMA+EN_STOP ; Remote DMA, Stop and reset the chipWait 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 longpauseBegin "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 ENDPORTDATATest 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 againCheck 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 headerNo 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_packetRead header of received packet.
rcv_ovr_rx_one: ; MOV R5,#0 ; R4R5 Remote Start Address Register PUSH AR4 ACALL ReadPacketHeader POP AR4Start 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 debrisIf 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 topPointer 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_okIf 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,AThen, 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 RegisterDelete 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 ringTurn off loopback mode.
; Take the NIC out of loopback PORTDATA EN0_TXCR, 0 ;Normal Operation ENDPORTDATAIf 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 transmitterAcknowledge interrupt and RETurn from this procedure.
AJMP recv_frame_breakNo buffer overflow, check for any received errors.
recv_no_overrun: JNB BITISR_COUNTERS, NoReadTallyCountersIf 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, PacketReceivedOtherwise, 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 requestIf 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 registersProcess 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_framesEnd of buffer reached, acknowledge interrupt and RETurn.
SJMP recv_frame_breakRead 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 ReadPacketHeaderTest if the received packet is OK.
MOV A,rcv_hdr ; Get the buffer status byte XRL A,#ENRSR_RXOK ; Good packet ? JZ GoodPacketOr, 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 RingIf 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 RETProcessing of a packet(*)
;************************************************************* ;** process frame ;** ProcessFrame: LCALL ProcessEthPacketPacket 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 bufferAnd, process a next packet, if any.
SJMP recv_framePacket 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.
| Web51 description | News | FAQ | ORDER FORM | DOWNLOAD | Links |
| (c)Copyright 2000 - 2002, HW server & Radek Benedikt
Web51@HW.cz, Web51.HW.cz |
|