Realtek Driver Problem

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
kemosparc
Member
Member
Posts: 207
Joined: Tue Oct 29, 2013 1:13 pm

Realtek Driver Problem

Post by kemosparc »

Hi,

I am having a problem with my realtek driver.

I was able to probe the PCI configuration and detect it, and read the mac address correctly.

Here is my code

Code: Select all

    io_base = pciConfigHeader->getPCIBar(PCI_BAR_IO) & ~1;
    mem_base = (uint8_t *)(pciConfigHeader->getPCIBar( PCI_BAR_MEM) & ~3);
    rx_buffer = (uint8_t *) kmalloc_ptr->kmalloc_a((uint64_t)20*FOURKILO);
    tx_buffers = (uint8_t *) kmalloc_ptr->kmalloc_a((uint64_t)32*FOURKILO);

	outb(Config1, 0x0);

	outb(ChipCmd, CmdReset);
	while((inb(ChipCmd) & CmdReset) != 0);
    video.putString("Realtek reset completed\n" ,COLOR_LIGHT_BROWN,COLOR_BLACK);

	Utils::memset(rx_buffer, 0, 20*FOURKILO);
	outl(RxBuf,(uint64_t)rx_buffer);
	outb(ChipCmd, 0xc);

    video.putString("Realtek set rx_buffer\n" ,COLOR_LIGHT_BROWN,COLOR_BLACK);


    for(int i=0; i < 4; i++)
		outl(TxAddr0 + i*4, ((uint64_t)tx_buffers) + i*(8192 +16+1500));

    video.putString("Realtek set tx_buffer\n" ,COLOR_LIGHT_BROWN,COLOR_BLACK);

    getMac();
    outw(IntrMask, 0x0005);
    outl(RxConfig,  AcceptBroadcast | AcceptMyPhys | AcceptAllPhys);

    register_interrupt_handler(IRQ0+pciConfigHeader->getIntLine(),&handler);
I call the above code like this

Code: Select all


   PCIConfigHeaderManager * pciConfigHeaderManager =  new PCIConfigHeaderManager();
    PCIConfigHeader * pciConfigHeader = pciConfigHeaderManager->getPCIDevice(0x10ec,0x8139);
    if ( pciConfigHeader != NULL )
    {
        RTL8139 * rtl8139 = new RTL8139(pciConfigHeader);
        rtl8139->start(); // This is the method that contains the above code.
    }


The problem is that the card does not fire any interrupts. I have both the keyboard and the pic timer working and firing interrupts.

The card is detected at interupt line 11 and interrupt_pin 1, and to force an interrupt to happen and execute the interrupt handler (which prints a string on the screen), I send an ethernet frame to the associated mac address and I can see the ethernet packet on the network using tcpdump from another machine.

I also added a print string inside the interrupt handler hub function printing the interrupt number before routing to the irq specific handler but this does not work except for the pic and the keyboard.

Any ideas what could be my problem and how can I enforce the interrupt to occur, especially that I did not implement the TCP layer in my OS yet.

Thanks
User avatar
bwat
Member
Member
Posts: 359
Joined: Fri Jul 03, 2009 6:21 am

Re: Realtek Driver Problem

Post by bwat »

kemosparc wrote:Any ideas what could be my problem and how can I enforce the interrupt to occur, especially that I did not implement the TCP layer in my OS yet.

Thanks
You don't need a TCP layer, just send an ethernet packet, any old packet, as long as its greater than the ethernet minimum. The realtek 8139 doesn't send an ethernet frame if it is too small, from memory it doesn't give you an error code or an interrupt either in this case, it just ignores the tx request. Also, the 8139 internal DMA needs the tx buffer on a 32 bit boundry IIRC.
Every universe of discourse has its logical structure --- S. K. Langer.
Nable
Member
Member
Posts: 453
Joined: Tue Nov 08, 2011 11:35 am

Re: Realtek Driver Problem

Post by Nable »

This may be unrelated to your issue but you'd better check: http://wiki.osdev.org/RTL8139#PCI_Bus_Mastering
rdos
Member
Member
Posts: 3306
Joined: Wed Oct 01, 2008 1:55 pm

Re: Realtek Driver Problem

Post by rdos »

My tip is that some ethernet cards do not have their IRQs connected to the legacy PIC, and that you need to use the APIC instead. Also, you could try to use MSI (provided this is supported). It's much easier, doesn't require PIC/APIC, and also doesn't need ACPI or IRQ detection.
kemosparc
Member
Member
Posts: 207
Joined: Tue Oct 29, 2013 1:13 pm

Re: Realtek Driver Problem

Post by kemosparc »

Hi,

I m using qemu, and I am using a host only network.

I don't know if there is a special setup regarding that, does qemu filter packets by any means?

Thanks
Kairm
kemosparc
Member
Member
Posts: 207
Joined: Tue Oct 29, 2013 1:13 pm

Re: Realtek Driver Problem

Post by kemosparc »

Hi,

Is there a way to trace inside qemu if the ether packe reached the VM or not and weather the Realtek card picked the packet and put it in the reception buffer?

Any suggestions are appreciated

Thanks
Karim.
User avatar
Pancakes
Member
Member
Posts: 75
Joined: Mon Mar 19, 2012 1:52 pm

Re: Realtek Driver Problem

Post by Pancakes »

You can always go straight to the source in QEMU - literally. If you compile QEMU which is fairly easy you could add some debug output letting you know. Even just reading the source will likely make it clear exactly what you need to do at least for QEMU.

Then there are the Linux drivers. Should be able to figure out what needs to be done by looking at the source.
kemosparc
Member
Member
Posts: 207
Joined: Tue Oct 29, 2013 1:13 pm

Re: Realtek Driver Problem

Post by kemosparc »

Hi,

I might be forced to do that.

To write the driver, I followed strictly the driver code of ChickeOS and minirighi32.

But since this is the first network device driver for me to write with my OS which does not have even a network stack layer, I have this basic question whose answer will help me a lot.

If I setup the card correctly and hooked it into my interrupt table, and my interrupt works already well (PIC and keyboard), it should fire an interrupt upon receiving an ether packet from the network on its mac address, right? I mean nothing in specific needs to be done except initializing the card and hooking it to its corresponding interrupt ? Then if I have a print out in the interrupt handler it should show, right?

Also, I would like to know if there is any restriction on the type of ether packets that I should use to test it?

Finally, what are the common problems that could disallow the card after initialization from firing the interrupt?

Would it be of any benefit if I post the code?

Thanks
Karim.
kemosparc
Member
Member
Posts: 207
Joined: Tue Oct 29, 2013 1:13 pm

Re: Realtek Driver Problem

Post by kemosparc »

Hi,

Okay, I have got the qmeu source, opened the debugging and compiled it.

Initially there was a problem with my device driver code, and the packets were not accepted and were not copied into the rx buffer.

My two main functions for intializing the rtl8139 are here:

Code: Select all


void RTL8139::reset ()
{

	unsigned i;
	Clock clock;
	uint64_t flags;
	save_flags( flags );

	outb(ChipCmd, CmdReset);
	cur_rx = 0;
	cur_tx = 0;
    enable();
	clock.setTimeout(10);
	while ( (inb(ChipCmd) & CmdReset) != 0 )
		if ( clock.isTimeout() )
			break;
	restore_flags( flags );

	for (int i=0; i<_countof(mac); i++)
        	outb(MAC0 + i,mac[i]);

	outb( ChipCmd, CmdRxEnb | CmdTxEnb);


    outl(RxConfig,
		(RX_FIFO_THRESH<<13) | (RX_BUF_LEN_IDX<<11) | (RX_DMA_BURST<<8));
	outl(TxConfig, (TX_DMA_BURST<<8) | 0x03000000);


	// Configure the RX buffer					//
	outl(RxBuf, (uint64_t) rx_phys);

	for (i = 0; i < NUM_TX_DESC; i++)
		outw(TxAddr0 + (i * 4), (uint64_t)(tx_phys + (TX_BUF_SIZE*i)));

	outw(RxMissed, 0);


	outb( ChipCmd, CmdRxEnb | CmdTxEnb);
//	outw( IntrMask, IntrDefault);
    outw( IntrMask, PCIErr | PCSTimeout | RxUnderrun |	RxOverflow | RxFIFOOver | TxErr | TxOK | RxErr | RxOK );

}


int RTL8139::start (bool _promisc)
{
	uint64_t flags;
    io_base = pciConfigHeader->getPCIBar(PCI_BAR_IO) & ~1;
    mem_base = (uint8_t *)(pciConfigHeader->getPCIBar( PCI_BAR_MEM) & ~3);

    if ( pciConfigHeader->get_pci_major() == 0x02 &&
    pciConfigHeader->get_pci_minor() == 0x00 &&
    pciConfigHeader->get_pci_interface() == 0x00)
        video.putString("Realtek 8139 correct device interface/class\n" ,COLOR_CYAN,COLOR_BLACK);
    else
    {
        video.putString("Realtek 8139 incorrect device interface/class\n" ,COLOR_RED,COLOR_BLACK);
        return -1;
    }
    local_irq_save( flags );

	outb(Config1, 0x00);
    register_interrupt_handler(IRQ0+pciConfigHeader->getIntLine(),&handler);


	// Read the MAC address						//
	if (read_eeprom(0) != 0xFFFF)
	{
		// Read from EEPROM					//
        video.putString("Reading mac from eeprom\n" ,COLOR_RED,COLOR_BLACK);
        unsigned short *ap = (unsigned short *)mac;
		for (int i = 0; i < 3; i++)
            *ap++ = read_eeprom(i + 7);
	}
	else
	{
        video.putString("Reading mac from pciConfig\n" ,COLOR_RED,COLOR_BLACK);
        getMac();
    }

    video.putString("Getting Media Information\n" ,COLOR_RED,COLOR_BLACK);
	speed10 = (inb(MediaStatus) & MSRSpeed10) != 0;
	fullduplex = (inw(MII_BMCR) & BMCRDuplex) != 0;

    if (speed10) video.putString("Speed: 10 Mb\n" ,COLOR_RED,COLOR_BLACK);
    else video.putString("Speed: 100 Mb\n" ,COLOR_RED,COLOR_BLACK);

    if (fullduplex) video.putString("Full Duplex\n" ,COLOR_RED,COLOR_BLACK);
    else video.putString("Half Duplex\n" ,COLOR_RED,COLOR_BLACK);

    promisc = _promisc;
    video.putString("Initialize the Rx and Tx rings buffer\n" ,COLOR_RED,COLOR_BLACK);

    rx_phys = (uint8_t *) kmalloc_ptr->kmalloc_a(RX_BUF_LEN);
    rx_ring = rx_phys;
    Utils::memset(rx_phys,0,RX_BUF_LEN);
    tx_phys = (uint8_t *) kmalloc_ptr->kmalloc_a(TX_BUF_SIZE*NUM_TX_DESC);
    tx_ring = tx_phys;
    Utils::memset(tx_phys,0,TX_BUF_SIZE*NUM_TX_DESC);


    reset ();

    if ( inb(TxStatus0) & MSRLinkFail)
        video.putString("Cable not connected or other link failure\n" ,COLOR_RED,COLOR_BLACK);
    else video.putString("Cable connected\n" ,COLOR_RED,COLOR_BLACK);

    outl(IntrStatus,0x1);
	local_irq_restore( flags );

    local_irq_save( flags );

    if ( promisc ) outb(RxConfig, AcceptBroadcast | AcceptMyPhys | AcceptAllPhys);
	else outb(RxConfig, AcceptBroadcast | AcceptMyPhys);

	local_irq_restore( flags );

    return 0;
}

The problem basically was with the last part for the "promisc" condition.
Basically when I execute this without the surrounding local_irq_save and local_irq_restore it overwrites the buffer length configuration command executed earlier in the reset function and all the packets are rejected because of size limitation.

Now, I can see from the debug output that the packets are copied into the buffer, but still the irq interrupt does not fire.
The buffer keeps on filling until it is full and refuse any more packets (Since the handler is not called and consume the buffer) which indicate that the buffering by qemu has no problem now.

I spent the past 2 days trying to find where the part responsible for firing the interupt in qemu source code, but I could not find it; no luck.

The PIC and the keyboard interrupts work fine, so I expect that there is nothing worng

Even if there is a problem woth my interrupt handler for rtl8139, I expect that the irq hub function is called and the couple of prints are executed before calling the rtl8139 handler.

Code: Select all

    if (ic->intNo == 43) // This condition is never true in my case and I expect that it should be if the rtl8139 interrupt fires
    {
        video.setPosition(30,10);
        video.putString("Realtec Inetrrupt",COLOR_RED,COLOR_LIGHT_BROWN);
    }

    if (interrupt_handlers[ic->intNo] != 0)
    {
        isr_t handler = interrupt_handlers[ic->intNo];
        handler();
    }
    else   // This else is never true in my case and I expect that it should be if the rtl8139 interrupt fires and there is no handler assigned for it
    {
        video.clearScreen(COLOR_RED);
        video.setPosition(30,10);
        if ( ic->intNo < IRQ0)
        {
            video.putString(exception_messages[ic->intNo],COLOR_RED,COLOR_LIGHT_BROWN);
            video.putString("[",COLOR_RED,COLOR_LIGHT_BROWN);
            video.putDecimal(ic->intNo,COLOR_RED,COLOR_LIGHT_BROWN);
            video.putString("]\n",COLOR_RED,COLOR_LIGHT_BROWN);
        }
        else
        {
            video.putString("Unhandled Interrupt",COLOR_RED,COLOR_LIGHT_BROWN);
            video.putString("[",COLOR_RED,COLOR_LIGHT_BROWN);
            video.putDecimal(ic->intNo,COLOR_RED,COLOR_LIGHT_BROWN);
            video.putString("]\n",COLOR_RED,COLOR_LIGHT_BROWN);
        }

        for (;;);
    }

    // Send an EOI (end of interrupt) signal to the PICs.
    // If this interrupt involved the slave.

    if (ic->intNo >= 40)
    {
        // Send reset signal to slave.
        Ports::outportb(PIC2_COMMAND, 0x20);
    }
    // Send reset signal to master. (As well as slave, if necessary).
    Ports::outportb(PIC1_COMMAND, 0x20);
I have added a couple of comments up there regarding the if conditions I expect to execute if the rtl8139 fires an interrupt.

I just can not proceed any more.

I have one question though, my card is detected at IRQ line 11, and IRQ Pin 1; I don't understand what the pin number indicates, does this affect anything in the way interrupts fire.

Any suggestions or help will be highly appreciated.

Thanks a lot
Karim.
Post Reply