Page 1 of 1

RTL8169 transmission

Posted: Wed Jun 23, 2010 11:41 pm
by JohnWilson
This is ridiculously specific but I'll bet some of you have been through the exact same thing. I'm writing a driver for RealTek RTL8169 NICs (my card has an RTL8169SB) and it's been mostly smooth sailing, except that in my tests, transmitted packets sometimes don't seem to get sent. Actually it likes what happens is, the receiving end times out (for testing I'm using a file transfer program that uses a simple proprietary stop-and-wait protocol) and asks for a retry, and at THAT point the missing packet suddenly gets sent, along with the retransmitted packet. I assumed it was a ring or interrupt problem (so the incoming packet was waking up the chip after I dropped the ball) but more testing shows that interrupts are working, and the TX packet's OWN bit is clearing fairly immediately (not a second later when the NAK packet comes in).

Sooooo ... I'm starting to get the impression that I've pissed off the PHYceiver in some way (maybe something to do with flow control???????), and that stuff is all greek to me. The RTL8169 wiki entry leaves the MII/GMII stuff as an exercise to the reader. I wrote code which mimics the OS/2 driver (whose logic is easier to follow than the Linux driver, and the commenting is no worse), which just tells the PHY to start auto-negotiation and polls it (with a timeout) until it reports that all is well (which IS happening). Any idea if I'm on the right track?

What blows my mind is, *reception* is the hard part of writing an Ethernet driver, and that stuff has worked perfectly almost since the beginning. Transmission should be a piece of cake -- it's always my idea (vs. RX which could happen at any time), and in this particular case the protocol is so simple that there's never more than one TX packet outstanding at a time (so there can't be a problem with my attempt to be cute by not telling the RTL8169 to poll the TX queue if the preceding descriptor's OWN bit is still set since I know help must be on the way).

I doubt anyone wants to see 1900 lines of 386 code (especially since it's full of macros and weird event queues) and I'm not sure what to post in the way of highlights. The init sequence is pretty similar to what's in the wiki. The routine to send packets is straightforward (constants should be mostly obvious, and XSER/SSER are routines which execute MFENCE etc. if this CPU has them):

Code: Select all

;+
;
; Send packet.
;
; edi   packet buffer from NETXBF -- DS:PK$LEN[EDI], DS:PK$CRT[EDI] set
; ebp   IF blk (preserved)
;
; On completion, a call is made at fork level to the routine pointed to by
; DS:PK$CRT[EDI] with EDI (which must be preserved) still set up to point at
; the packet buffer, and EBP (which also must be preserved) pointing at the
; RTL8169 IF block.
;
;-
r69spk: xor     eax,eax         ;load 0
        mov     ds:pk$flg[edi],al ;init flags
        mov     ds:pk$nxt[edi],eax ;end of queue
        mov     ebx,ss:ethxlq[ebp] ;get existing end of queue
        mov     ss:ethxlq[ebp],edi ;now it's us
        test    ebx,ebx         ;was there anything?
        jnz     short @@1       ;yes
        mov     ss:ethxcq[ebp],edi ;no, we're at head too
        jmp     short @@2
@@1:    mov     ds:pk$nxt[ebx],edi ;link us to previous tail
@@2:    ; TX descriptor ring is big enough for all our bufs at once
        ; (so we can always add another immediately -- no queue to get in)
.assume <xring ge r69$xb*dsize>
        ; fill in descriptor
        mov     ebx,ss:rtltxf[ebp] ;get "fill" pointer
        mov     ecx,ds:pk$len[edi] ;get length
        lea     esi,[ebx+dsize] ;get addr that follows
        or      ecx,d$own+d$fs+d$ls ;owned by NIC, first/last seg
        cmp     esi,xring       ;final descriptor before wrap?
        jne     short @@3
        or      ecx,d$eor       ;end of ring (wrap back to beginning)
        xor     esi,esi         ;wrap offset back to beginning too
@@3:    mov     ss:rtltxf[ebp],esi ;update "fill" pointer
        add     ebx,ss:rtlxva[ebp] ;add base VA of TX descriptor ring
        xor     edx,edx         ;load 0
        mov     eax,ds:pk$pa[edi] ;get physical address for this buf
        mov     ds:dbuf+4[ebx],edx ;zero-extend addr
        mov     ds:dbuf[ebx],eax ;save addr itself
        mov     dword ptr ds:dvlan[ebx],edx ;VLAN stuff = 0
.assume <dflg2 eq dvlan+2>
        call    sser            ;serialize stores
        mov     ds:dflen[ebx],ecx ;set D$OWN last
                                ;(in case RTL8169 was about to check it anyway)
        ; see if NIC has eaten preceding frame yet
        ; (if not, no need to tell it to poll, since it'll get to us next)
        sub     esi,dsize*2     ;back to descriptor before ours
        and     esi,xring-1     ;(wrap back to end if needed)
.assume <(xring and (xring-1)) eq 0> ;(must be power of two)
        add     esi,ss:rtlxva[ebp] ;add base VA of TX descriptor ring
        call    xser            ;(serialize loads and stores)
        test    byte ptr ds:dflen+3[esi],d$own shr 24d ;owned by NIC?
.assume <(d$own and (not 0FF000000h)) eq 0> ;(D$OWN is in the high byte)
        jnz     short @@4       ;yes, no need to poke it
        mov     ebx,ss:rtlreg[ebp] ;get pointer to reg window
        mov     byte ptr ds:tppoll[ebx],tp$npq ;poll normal priority queue
@@4:    ret
As I say though, the symptoms suggest that the problem is happening after the packet has already gone into the FIFO and the TX descriptor has been given back. But please tell me how wrong I am!!! I've been bashing my head against this for days and days with no useful progress.

Thanks,

John Wilson
D Bit

Re: RTL8169 transmission

Posted: Mon Jun 28, 2010 1:54 pm
by JohnWilson
OK I found it and it was just a dumb interrupt bug. The RealTek doc is really bare-bones and I made an incorrect assumption about the interaction between the IMR and ISR regs. My int service routine was clearing IMR and scheduling a routine to run at "fork" level which would check the ISR bits and then rewrite IMR on exit, figuring that any new ints in the mean time would cause a fresh int. OK so I have no idea what the normal concise way to say "fork level" is -- what I mean is that the real work of processing the interrupt is done in a routine called (with ints enabled) through a software queue which serializes all calls and can preempt the mainline but nothing else, so that the int service routine itself is only a dozen or so instructions long. I learned this method from DEC PDP-11 OSes but there might be a more common (i.e. Linuxy) name for it (I think DEC was just looking for an excuse to say "fork queue" in their manuals).

Anyway, so I was imagining that the hardware ANDs the IMR and ISR bits together, and the OR of all that is what triggers a PCI interrupt. It appears that what really happens is that a 0-1 transition on an ISR bit causes the corresponding IMR bit to be clocked into a flip-flop (or whatever) for that bit, and all those FFs' outputs are ORed together to drive the int. Or something. Anyway the point is, if the ISR bit was already set when you set its matching IMR bit, you *don't* get an int. Which is obvious if you can read the designer's mind (or happen to think like him anyway) but not if you can't.

John Wilson
D Bit

Re: RTL8169 transmission

Posted: Mon Jun 28, 2010 6:19 pm
by Matthew
Thanks for the description. The RTL8169 sounds similar in design to some other NICs I have worked with.

I think the term you are searching for is "bottom half".

Re: RTL8169 transmission

Posted: Mon Jun 28, 2010 11:30 pm
by JohnWilson
Aha! I'd heard the expression but didn't know what it referred to. Thanks!

John Wilson
D Bit

Re: RTL8169 transmission

Posted: Mon Jan 11, 2016 2:47 am
by manojsingh
I had written the driver for rtl8139 and its working but here in forum all the code for rtl8169 is in asm can any give snippet of transmission code in c for sending packet and receiving packet . and the code in this forum also in asm . will anyone convert and send the snippet.
thanks everyone

Re: RTL8169 transmission

Posted: Mon Jan 11, 2016 2:57 am
by Combuster
It's generally not a good idea to
- necro a topic
- post offtopic
- ask people to write code for you.

Please read the forum rules, in particular the reference on "smart questions", then try again by making a proper thread.