RTL8169: Difference between revisions

1,543 bytes added ,  29 days ago
m
Bot: Replace deprecated source tag with syntaxhighlight
[unchecked revision][unchecked revision]
(Category Rename)
m (Bot: Replace deprecated source tag with syntaxhighlight)
 
(16 intermediate revisions by 11 users not shown)
Line 1:
{{Stub}}
 
'''Programming Guide for the RTL8169(S)-32/64 Network Interface Chipsets'''
 
The RTL8169 is Realtek's next generation of high-performance network cards. This particular chipset is designed to operate at 10/100/1000 Mbps speeds.
 
== Identification ==
The basic interface for the RTL8169 series is common in several Realtek network cards. This applies to the following noncomprehensive list of PCI Vendor and Device IDs from FreeBSD's re driver:
* 10ec:8161
* 10ec:8168
* 10ec:8169
* 1259:c107
* 1737:1032
* 16ec:0116
 
== Basic Startup ==
Line 12 ⟶ 19:
 
ex:
<syntaxhighlight lang="c">
char i;
for (i = 0; i < 6; i++)
Line 17 ⟶ 25:
mac_address[i] = inportb(ioaddr + i); /*ioaddr is the base address obtainable from the PCI device configuration space.*/
}
</syntaxhighlight>
 
=== Reset the Chip ===
Line 23 ⟶ 32:
 
ex:
<syntaxhighlight lang="c">
outportb(ioaddr + 0x37, 0x10); /*set the Reset bit (0x10) to the Command Register (0x37)*/
while(inportb(ioaddr + 0x37) & 0x10)
;/*setting a timeout could be useful if the card is problematic*/
</syntaxhighlight>
 
== Setting up the Rx Descriptors ==
One of the new features introduced with this new-gen chip is that it is completely descriptor-based. The chip supports up to 3 unique descriptor sets with 1024 descriptors available per set; 1024 for receiving packets, 1024 for normal transmission, and 1024 for high-priority transmission packets. It is not required to use all of the descriptors per set, or even use all three sets. Each descriptor is 16-bytes wide and contains a 64-bit buffer address (to store data for transmission or to tell the reception process where to dump the data) and Command double-worduint32_t.
 
Rx Descriptors are used to tell the NIC where to put packets once received. Because this is an asynchronous operation, we must setup the descriptors before we enable the reception of packets so that the NIC knows where to put things. A major function of descriptors is to tell the NIC which descriptors are owned by the OS (host) and which ones are owned by the NIC. If the descriptor is owned by the NIC, the NIC can then read it and use it for the next packet it receives, if not, it either skips over it or sends a RDU (Rx Descriptor Unavailable) interrupt and halts all further Rx operations. Because there can be multiple descriptors used, there is also a EOR (End of Rx descriptor Ring) bit used when the NIC has reached the end of the descriptors and needs to loop back to the beginning of the descriptors. There is one more big 'gotcha' with all of this, the beginning of the descriptor arrays must be aligned on a 256-byte boundary.
 
====Descriptor format====
eg:
{| {{wikitable}}
struct Descriptor
! Offset
{
! Name
unsigned int command, /* command/status dword */
! Description
vlan, /* currently unused */
|-
low_buf, /* low 32-bits of physical buffer address */
| dword 0 || Flags || 31 bit=OWN (if set, card own this descriptor) 30 bit=EOR (last descriptor in list)
high_buf; /* high 32-bits of physical buffer address */
|-
| dword 1 || VLAN || Unused
|-
| dword 2 || Pointer to buffer low 32 bits ||
|-
| dword 3 || Pointer to buffer high 32 bits ||
|-
|}
 
====Code example====
<syntaxhighlight lang="c">
struct Descriptor {
unsigned intuint32_t command,; /* command/status dworduint32_t */
uint32_t vlan,; /* currently unused */
uint32_t low_buf,; /* low 32-bits of physical buffer address */
uint32_t high_buf; /* high 32-bits of physical buffer address */
};
Line 60 ⟶ 87:
}
}
</syntaxhighlight>
 
== Configuring RxConfig and TxConfig ==
Line 69 ⟶ 97:
 
eg:
<syntaxhighlight lang="c">
outportl(ioaddr + 0x40, 0x03000700); /* IFG: normal, MXDMA: unlimited */
</syntaxhighlight>
 
=== RxConfig ===
Line 75 ⟶ 105:
 
eg:
<syntaxhighlight lang="c">
outportl(ioaddr + 0x44, 0x0000E70F) /* RXFTH: unlimited, MXDMA: unlimited, AAP: set (promisc. mode set) */
</syntaxhighlight>
 
== Max Packet Sizes ==
=== Max Transmit Packet Size ===
Line 82 ⟶ 113:
 
eg:
<syntaxhighlight lang="c">
outportb(ioaddr + 0xEC, 0x3B); /* Maximum tx packet size */
</syntaxhighlight>
 
=== Receive Packet Maximum Size ===
The RMS register is located at offset 0xDA and is used for setting the maximum packet size that can be received into the NIC. If set to zero, the NIC will accept no packets (must be set prior to normal operation!). The maximum size that can be set is 0x3FFF (16k - 1), but if a packet larger than 8k is received error bits are set and a lengthier process must be started in order to determine if the packet is actually good or not. I keep this value to 0x1FFF (8k - 1) as it is rare to receive such large packets and it also ensures that error packets are actually error packets and not false positives.
 
eg:
<syntaxhighlight lang="c">
outportw(ioaddr + 0xDA, 0x1FFF); /* Maximum rx packet size */
</syntaxhighlight>
 
== Full Reset Example ==
This is a barebones example of how to reset the RTL8169 without anything like auto-negotiation (explained later) or (G)MII interactions.
 
eg:
<syntaxhighlight lang="c">
struct Descriptor
{
unsigned intuint32_t command,; /* command/status dworduint32_t */
uint32_t vlan,; /* currently unused */
uint32_t low_buf,; /* low 32-bits of physical buffer address */
uint32_t high_buf; /* high 32-bits of physical buffer address */
};
 
/**
* Note that this assumes 16*1024=16KB (4 pages) of physical memory at 1MB and 2MB is identity mapped to
* the same linear address range
*/
struct Descriptor *Rx_Descriptors = (struct Descriptor *)0x100000; /* 1MB Base Address of Rx Descriptors */
struct Descriptor *Tx_Descriptors = (struct Descriptor *)0x200000; /* 2MB Base Address of Tx Descriptors */
Line 118 ⟶ 156:
else
Rx_Descriptors[i].command = (OWN | (rx_buffer_len & 0x3FFF));
 
Rx_Descriptors[i].low_buf = (unsigned int)&packet_buffer_address; /* This is where the packet data will go */
/** packet_buffer_address is the *physical* address for the buffer */
Rx_Descriptors[i].low_buf = (unsigned int)&packet_buffer_address; /* This is where the packet data will go */
Rx_Descriptors[i].high_buf = 0;
/* If you are programming for a 64-bit OS, put the high memory location in the 'high_buf' descriptor area */
}
Line 137 ⟶ 178:
outportb(ioaddr + 0x50, 0xC0); /* Unlock config registers */
outportl(ioaddr + 0x44, 0x0000E70F); /* RxConfig = RXFTH: unlimited, MXDMA: unlimited, AAP: set (promisc. mode set) */
outportb(ioaddr + 0x37, 0x04); /* Enable Tx in the Command register, required before setting TxConfig */
outportl(ioaddr + 0x40, 0x03000700); /* TxConfig = IFG: normal, MXDMA: unlimited */
outportw(ioaddr + 0xDA, 0x1FFF); /* Max rx packet size */
Line 142 ⟶ 184:
/* offset 0x20 == Transmit Descriptor Start Address Register
offset 0xE4 == Receive Descriptor Start Address Register */
outportl(ioaddr + 0x20, (unsigned long)&Tx_Descriptors[0]; /* Tell the NIC where the first Tx descriptor is */
Again, these are *physical* addresses. This code assumes physical==linear, this is
outportl(ioaddr + 0xE4, (unsigned long)&Rx_Descriptors[0]; /* Tell the NIC where the first Rx descriptor is */
typically not the case in real world kernels
*/
outportl(ioaddr + 0x20, (unsigned long)&Tx_Descriptors[0]; /* Tell the NIC where the first Tx descriptor is. NOTE: If writing a 64-bit address, split it into two 32-bit writes.*/
outportl(ioaddr + 0xE4, (unsigned long)&Rx_Descriptors[0]; /* Tell the NIC where the first Rx descriptor is. NOTE: If writing a 64-bit address, split it into two 32-bit writes.*/
outportb(ioaddr + 0x37, 0x0C); /* Enable Rx/Tx in the Command register */
outportb(ioaddr + 0x50, 0x00); /* Lock config registers */
}
</syntaxhighlight>
 
== External Links ==
 
* http://wwwrealtek.magnesium.netinfo/~wpaulpdf/rt/RTL8110S_8169S_DataSheet_1.3rtl8169s.pdf, Datasheet for the RTL8169S and RTL8110S chpsetschipsets.
 
[[Category:Network Hardware]]