RTL8139: Difference between revisions

5,759 bytes added ,  29 days ago
m
Bot: Replace deprecated source tag with syntaxhighlight
[unchecked revision][unchecked revision]
No edit summary
m (Bot: Replace deprecated source tag with syntaxhighlight)
 
(40 intermediate revisions by 20 users not shown)
Line 1:
{{StubIn Progress}}
 
The RTL8139 Network Chip is used on many old and budget Ethernet Network Devices. It supports 10 and 100 MBit.
 
While the original chip lacks the features of newer Gigabit devices, it is a commonly emulated device in virtualization environments, due to its simplicity (fewer IO operations means faster traps to the Hypervisor), and extensive OS support.
 
It also makes a for a simple driver (and so is an excellent first choice for OS development hobbyists).
 
[[Image:rtl8139.JPG|right|frame|A PCI RTL8139]]
 
NB If you find your driver suddenly freezes and stops receiving interrupts and you're using kvm/qemu. Try the option -no-kvm-irqchip
 
The PCI vendor ID is 0x10EC and the device ID is 0x8139.
 
== Overview ==
 
RTL8139 is configured via registers. The IO base address and the IRQ number for the device is taken from [[PCI]] configuration.
 
 
=== Registers ===
Line 21 ⟶ 30:
|-
| 0x08 || 8 || MAR0-7
|-
| 0x30 || 4 || RBSTART
|-
| 0x37 || 1 || CMD
Line 30 ⟶ 41:
 
== Programming Guide ==
 
=== PCI Bus Mastering ===
 
First, you need to enable PCI Bus Mastering for this device. This allows the NIC to perform DMA. To do it, you can read the Command Register from the device's PCI Configuration Space, set bit 2 (bus mastering bit) and write the modified Command Register. Note this Command Register is that of the PCI Configuration Space and has nothing to do with the Command Register that will be evoked in the following sections: the latter is specific to the RTL8139, whereas every PCI device (not only NICs) have a PCI Configuration Space with a Command Register.
 
Some BIOS may enable Bus Mastering at startup, but some versions of qemu don't. You should thus be careful about this step.
 
=== Turning on the RTL8139 ===
Line 35 ⟶ 52:
Send 0x00 to the CONFIG_1 register (0x52) to set the LWAKE + LWPTN to active high.
this should essentially *power on* the device.
 
<syntaxhighlight lang="c">
outportb( ioaddr + 0x52, 0x0);
</syntaxhighlight>
 
==== Software Reset! ====
Line 45 ⟶ 66:
Once that byte is sent, the RST bit must be checked to make sure that the chip has finished
the reset. If the RST bit is high (1), then the reset is still in operation.
 
NB: There is a minor bug in Qemu. If you check the command register before performing a
soft reset, you may find the RST bit is high (1). Just ignore it and carry on with the
initialization procedure.
 
<syntaxhighlight lang="c">
outportb( ioaddr + 0x37, 0x10);
while( (inb(ioaddr + 0x37) & 0x10) != 0) { }
</syntaxhighlight>
 
==== Init Receive buffer ====
Line 52 ⟶ 82:
that variables memory location to the RBSTART register (0x30).
 
<syntaxhighlight lang="c">
ex:
//ioaddr is obtained from PCI configuration
char rx_buffer[8192+16]; // declare the local buffer space (8k + header)
outportd(ioaddr + 0x30, (unsigned longuintptr_t)rx_buffer); // send dworduint32_t memory location to RBSTART (0x30)
</syntaxhighlight>
 
Note that 'rx_buffer' needs to be a pointer to a '''physical address'''. In this case a size of 8192 + 16 (8K + 16 bytes) is recommended, see below.
 
=== Set IMR + ISR ===
Line 67 ⟶ 100:
a TOK or ROK IRQ happens, it actually will go through and fire up an IRQ.
 
<syntaxhighlight lang="c">
ex:
outportw(ioaddr + 0x3C, 0x0005); // Sets the TOK and ROK bits high
</syntaxhighlight>
 
NB: When you handle an interrupt, you ''have'' to write the bit corresponding to the interrupt to reset it. The doc says reading the register is enough to reset the buffer to zero and writing has no effect. ''This is not the case on QEmu'', and probably on some/most hardware too. Writing a bit when it has no effect will probably not hurt.
 
01000101: Confirmed. In fact, the only way to clear an interrupt is by writing to it. The datasheet says that reading is what you must do, but it is completely wrong.
 
=== Configuring receive buffer (RCR) ===
Line 80 ⟶ 110:
You can enable different "matching" rules:
* '''AB - Accept Broadcast''': Accept broadcast packets sent to mac ff:ff:ff:ff:ff:ff
* '''AM - Accept Multicast''': Accept multicast packets.
* '''APM - Accept PartialPhysical Match''': Accept packets partiallysend matchedto forNIC's MAC youaddress.
* '''AAP''': - Accept exactAll matchPackets'''. ThisAccept isall onepackets you usually don't(run wantin topromiscuous forgetmode).
 
Another bit, the WRAP bit, controls the handling of receive buffer wrap around.
You can also enable a nice option in this register : the WRAP option. It enables the Rx buffer to act as a ring buffer: if a packet is being written near the end of the buffer and the RTL8139 knows you've already handled data before this (thanks to CAPR), the packet will continue at the beginning of the buffer.
 
If WRAP is 0, the Rx buffer is treated as a traditional ring buffer: if a packet is being written near the end of the buffer and the RTL8139 knows you've already handled data before this (thanks to CAPR), the packet will continue at the beginning of the buffer.
You can also tell the size of your RX buffer here, however if you use a 8k + 16 buffer as described before, writing zeroes is enough.
 
If WRAP is 1, the remainder of the packet will be written contiguously (overflowing the actual receive buffer) so that it can be handled more efficiently. This means the buffer must be an additional 1500 bytes (to hold the largest potentially overflowing packet).
Example:
 
outportl(0x44, 0xf | (1 << 7)); // (1 << 7) is the WRAP bit, 0xf is AB+AM+APM+AAP
You can also tell the size of your RX buffer here, however if you use a 8k + 16 buffer as described before, writing zeroes is enough. To use the WRAP=1 bit, an 8K buffer must in fact be 8k+16+1500 bytes.
 
<syntaxhighlight lang="c">
outportl(ioaddr + 0x44, 0xf | (1 << 7)); // (1 << 7) is the WRAP bit, 0xf is AB+AM+APM+AAP
</syntaxhighlight>
 
=== Enable Receive and Transmitter ===
Line 103 ⟶ 138:
Once this is completed, then the card will start allowing packets in and/or out.
 
<syntaxhighlight lang="c">
ex:
outportb(ioaddr + 0x37, 0x0C); // Sets the RE and TE bits high
</syntaxhighlight>
 
=== Transmitting Packets ===
The RTL8139 NIC uses a round robin style for transmitting packets. It has four transmit buffer (a.k.a. transmit start) registers, and four transmit status/command registers. The transmit start registers are each 32 bits long, and are in I/O offsets 0x20, 0x24, 0x28 and 0x2C. The transmit status/command registers are also each 32 bits long and are in I/O offsets 0x10, 0x14, 0x18 and 0x1C. Each pair of transmit start and status registers work together (i.e. registers 0x20 and 0x10 work together, 0x24 and 0x14 work together, etc.)
 
After performing a controller reset as mentioned above, the controller ignores all transmit registers except pair zero (i.e. register 0x20 for the transmit start and register 0x10 for the status/command.) After software transmits a packet using those registers, the round robin counter increments, to use pair one (i.e. register 0x24 for the transmit start and register 0x14 for the status/command.) This continues until pair number three, which is the last transmit register pair, and the counter then overflows and goes back to pair number zero. When the controller is using a specific transmit register pair, all other transmit registers are ignored, and cannot be used to transmit packets at all. Software is responsible for keeping track which pair is being used.
 
Each transmit start register contains the 32-bit physical address of the packet which is to be sent over the network.
 
Each transmit status/command register contains various bit fields that control the transmission of the packet. The table below only gives a brief about these bits. For the full format, take a look at the RTL8139 specification under "External links" below.
 
{| width="70%" border="1"
|-
|'''Bit Range'''
|'''Description'''
|-
|0-12
|'''Data size.''' This field contains the total size of the data to be sent across the network. The maximum value for this field is 1792, and larger numbers will cause an error.
|-
|13
|'''Own bit.''' When this bit is set to 1, the controller is idle and not performing any DMA transfers on this descriptor. The driver sets this bit to zero to tell the controller to start transmitting the packet. The bit then remains zero until the entire packet has been copied from RAM into the controller's internal memory via DMA. The default value after a reset is one. Setting this bit to zero also clears all other status bits in this register.
|-
|15
|'''Transmit OK.''' After the own bit has been set by the hardware, indicating the DMA transfer has completed, the hardware will start to transmit the packet across the actual network. This bit will be set to one after the network transmission has completed.
|}
 
== ISR Handler ==
When you handle an interrupt, you ''have'' to write the bit corresponding to the interrupt to reset it. The datasheet says reading the register is enough to reset the buffer to zero and writing has no effect. ''This is not the case on QEMU'', and probably on some/most hardware too.
 
'''Note that it is important you write to this register ''before you read any packets from your buffers'', or the write to the register will have no effect, and any other packets than the first will not be delivered to your ISR.'''
 
For example, this is tested and works on QEmu:
 
<syntaxhighlight lang="c">
void rtl8139_handler(uint8_t isr, uint64_t error, uint64_t irq) {
uint16_t status = inw(io_base + 0x3e);
outw(io_base + 0x3E, 0x05);
if(status & TOK) {
// Sent
}
if (status & ROK) {
// Received
receive_packet();
}
}
</syntaxhighlight>
 
== Related Articles ==
Line 112 ⟶ 193:
== External Links ==
 
* [https://github.com/omarrx024/xos/tree/master/rtl8139 xOS RTL8139 Driver] (MIT license)
* http://www.magnesium.net/~wpaul/rt/spec-8139cp(150).pdf, Datasheet for the RTL8139
* http://www.magnesium.net/~wpaul/rt/spec-8139cp(150).pdf, Datasheet for the RTL8139C
* http://www.cs.usfca.edu/~cruse/cs326f04/RTL8139D_DataSheet.pdf, Datasheet for the RTL8139D, has more information
* http://www.cs.usfca.edu/~cruse/cs326f04/RTL8139_ProgrammersGuide.pdf, Programming guide for the RTL8139
* http://www.jbox.dk/sanos/source/sys/dev/rtl8139.c.html, Example Driver implementation (GPL Licensed)
 
[[Category:Network devicesHardware]]
[[Category:Standards]]
[[de:RTL8139]]