metalsvm/drivers/net/rtl8139.c
2011-08-02 15:58:20 +02:00

449 lines
13 KiB
C

/*
* Copyright 2010 Stefan Lankes, Chair for Operating Systems,
* RWTH Aachen University
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* This file is part of MetalSVM.
*
* This code based mostly on the online manual http://www.lowlevel.eu/wiki/RTL8139
*/
#include <metalsvm/stddef.h>
#include <metalsvm/stdio.h>
#include <metalsvm/string.h>
#include <metalsvm/processor.h>
#include <metalsvm/mailbox.h>
#include <metalsvm/page.h>
#include <asm/io.h>
#include <asm/irq.h>
#if defined(CONFIG_LWIP) && defined(CONFIG_PCI)
#include <asm/pci.h>
#include <lwip/sys.h>
#include <lwip/stats.h>
#include <lwip/netif.h>
#include <netif/etharp.h>
#include <net/rtl8139.h>
board_t board_tbl[] =
{
{"RealTek", "RealTek RTL8139", 0x10ec, 0x8139},
{"RealTek", "RealTek RTL8129 Fast Ethernet", 0x10ec, 0x8129},
{"RealTek", "RealTek RTL8139B PCI", 0x10ec, 0x8138},
{"SMC", "SMC1211TX EZCard 10/100 (RealTek RTL8139)", 0x1113, 0x1211},
{"D-Link", "D-Link DFE-538TX (RTL8139)", 0x1186, 0x1300},
{"LevelOne", "LevelOne FPC-0106Tx (RTL8139)", 0x018a, 0x0106},
{"Compaq", "Compaq HNE-300 (RTL8139c)", 0x021b, 0x8139},
{NULL,},
};
static struct netif* mynetif = NULL;
/*
* @return error code
* - ERR_OK: packet transferred to hardware
* - ERR_CONN: no link or link failure
* - ERR_IF: could not transfer to link (hardware buffer full?)
*/
static err_t rtl8139if_output(struct netif* netif, struct pbuf* p)
{
rtl1839if_t* rtl8139if = netif->state;
uint8_t transmitid = rtl8139if->tx_queue % 4;
uint32_t i;
struct pbuf *q;
if (BUILTIN_EXPECT((rtl8139if->tx_queue - rtl8139if->tx_complete) > 3, 0)) {
LWIP_DEBUGF(NETIF_DEBUG, ("rtl8139if_output: too many packets at once\n"));
return ERR_IF;
}
if (BUILTIN_EXPECT(p->tot_len > 1792, 0)) {
LWIP_DEBUGF(NETIF_DEBUG, ("rtl8139if_output: packet is longer than 1792 bytes\n"));
return ERR_IF;
}
if (rtl8139if->tx_inuse[transmitid] == 1) {
LWIP_DEBUGF(NETIF_DEBUG, ("rtl8139if_output: %i already inuse\n", transmitid));
return ERR_IF;
}
if (inportb(rtl8139if->iobase + MSR) & MSR_LINKB) {
LWIP_DEBUGF(NETIF_DEBUG, ("rtl8139if_output: link failure\n"));
return ERR_CONN;
}
rtl8139if->tx_queue++;
rtl8139if->tx_inuse[transmitid] = 1;
#if ETH_PAD_SIZE
pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
#endif
/*
* q traverses through linked list of pbuf's
* This list MUST consist of a single packet ONLY
*/
for (q = p, i = 0; q != 0; q = q->next) {
memcpy(rtl8139if->tx_buffer[transmitid] + i, q->payload, q->len);
i += q->len;
}
// send the packet
outportl(rtl8139if->iobase + TSD0 + (4 * transmitid), p->tot_len);//|0x3A0000);
#if ETH_PAD_SIZE
pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
#endif
LINK_STATS_INC(link.xmit);
return ERR_OK;
}
static void rtl8139if_input(struct netif* netif, struct pbuf* p)
{
struct eth_hdr *ethhdr;
/* points to packet payload, which starts with an Ethernet header */
ethhdr = p->payload;
switch (htons(ethhdr->type)) {
/* IP or ARP packet? */
case ETHTYPE_ARP:
case ETHTYPE_IP:
#if PPPOE_SUPPORT
/* PPPoE packet? */
case ETHTYPE_PPPOEDISC:
case ETHTYPE_PPPOE:
#endif /* PPPOE_SUPPORT */
/* full packet send to tcpip_thread to process */
if (mynetif->input(p, mynetif) != ERR_OK) {
LWIP_DEBUGF(NETIF_DEBUG, ("rtl8139if_input: IP input error\n"));
pbuf_free(p);
}
break;
default:
pbuf_free(p);
break;
}
}
static void rtl_rx_inthandler(struct netif* netif)
{
rtl1839if_t* rtl8139if = netif->state;
uint16_t header;
uint16_t length, i;
uint8_t cmd;
struct pbuf *p = NULL;
struct pbuf* q;
cmd = inportb(rtl8139if->iobase + CR);
while(!(cmd & CR_BUFE)) {
header = *((uint16_t*) (rtl8139if->rx_buffer+rtl8139if->rx_pos));
rtl8139if->rx_pos = (rtl8139if->rx_pos + 2) % (8192+16);
if (header & ISR_ROK) {
length = *((uint16_t*) (rtl8139if->rx_buffer+rtl8139if->rx_pos)) - 4; // copy packet (but not the CRC)
rtl8139if->rx_pos = (rtl8139if->rx_pos + 2) % (8192+16);
#if ETH_PAD_SIZE
length += ETH_PAD_SIZE; /* allow room for Ethernet padding */
#endif
p = pbuf_alloc(PBUF_RAW, length, PBUF_POOL);
if (p) {
#if ETH_PAD_SIZE
pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
#endif
for (q=p; q!=NULL; q=q->next) {
for(i=0; i<q->len; i++) {
((uint8_t*) q->payload)[i] = rtl8139if->rx_buffer[rtl8139if->rx_pos];
rtl8139if->rx_pos = (rtl8139if->rx_pos + 1) % (8192+16);
}
}
#if ETH_PAD_SIZE
pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
#endif
rtl8139if_input(netif, p);
LINK_STATS_INC(link.recv);
} else {
LWIP_DEBUGF(NETIF_DEBUG, ("rtl8139if_rx_inthandler: not enough memory!\n"));
rtl8139if->rx_pos += (rtl8139if->rx_pos + length) % (8192+16);
LINK_STATS_INC(link.memerr);
LINK_STATS_INC(link.drop);
}
// packets are dword aligned
rtl8139if->rx_pos = ((rtl8139if->rx_pos + 4 + 3) & ~0x3) % (8192+16);
outportw(rtl8139if->iobase + CAPR, rtl8139if->rx_pos - 0x10);
} else {
LWIP_DEBUGF(NETIF_DEBUG, ("rtl8139if_rx_inthandler: invalid header!\n"));
LINK_STATS_INC(link.memerr);
LINK_STATS_INC(link.drop);
break;
}
cmd = inportb(rtl8139if->iobase + CR);
}
}
static void rtl_tx_inthandler(struct netif* netif)
{
rtl1839if_t* rtl8139if = netif->state;
uint32_t checks = rtl8139if->tx_queue - rtl8139if->tx_complete;
uint32_t txstatus;
uint8_t tmp8;
while(checks > 0)
{
tmp8 = rtl8139if->tx_complete % 4;
txstatus = inportl(rtl8139if->iobase + TSD0 + tmp8 * 4);
if (!(txstatus & (TSD_TOK|TSD_TUN|TSD_TABT)))
return;
if (txstatus & (TSD_TABT | TSD_OWC)) {
LWIP_DEBUGF(NETIF_DEBUG, ("rtl8139_tx_inthandler: major error\n"));
continue;
}
if (txstatus & TSD_TUN) {
LWIP_DEBUGF(NETIF_DEBUG, ("rtl8139_tx_inthandler: transmit underrun\n"));
}
if (txstatus & TSD_TOK) {
rtl8139if->tx_inuse[tmp8] = 0;
rtl8139if->tx_complete++;
checks--;
}
}
}
static void rtl8139if_handler(struct state* s)
{
rtl1839if_t* rtl8139if = mynetif->state;
uint16_t isr_contents;
while (1) {
isr_contents = inportw(rtl8139if->iobase + ISR);
if (isr_contents == 0)
break;
if (isr_contents & ISR_ROK) {
rtl_rx_inthandler(mynetif);
outportw(rtl8139if->iobase + ISR, ISR_ROK);
}
if (isr_contents & ISR_TOK) {
rtl_tx_inthandler(mynetif);
outportw(rtl8139if->iobase + ISR, ISR_TOK);
}
if (isr_contents & ISR_RER) {
LWIP_DEBUGF(NETIF_DEBUG, ("rtl8139if_handler: RX error detected!\n"));
outportw(rtl8139if->iobase + ISR, ISR_RER);
}
if (isr_contents & ISR_TER) {
LWIP_DEBUGF(NETIF_DEBUG, ("rtl8139if_handler: TX error detected!\n"));
outportw(rtl8139if->iobase + ISR, ISR_TER);
}
if (isr_contents & ISR_RXOVW) {
LWIP_DEBUGF(NETIF_DEBUG, ("rtl8139if_handler: RX overflow detected!\n"));
outportw(rtl8139if->iobase + ISR, ISR_RXOVW);
}
}
}
err_t rtl8139if_init(struct netif* netif)
{
rtl1839if_t* rtl8139if;
uint32_t tmp32;
uint16_t tmp16, speed;
uint8_t tmp8;
static uint8_t num = 0;
//task_t* task = per_core(current_task);
LWIP_ASSERT("netif != NULL", (netif != NULL));
rtl8139if = kmalloc(sizeof(rtl1839if_t));
if (!rtl8139if) {
LWIP_DEBUGF(NETIF_DEBUG, ("rtl8139if_init: out of memory\n"));
return ERR_MEM;
}
memset(rtl8139if, 0, sizeof(rtl1839if_t));
/* allocate the receive buffer */
rtl8139if->rx_buffer = mem_allocation(8192+16, MAP_KERNEL_SPACE|MAP_NO_CACHE);
if (!(rtl8139if->rx_buffer)) {
LWIP_DEBUGF(NETIF_DEBUG, ("rtl8139if_init: out of memory\n"));
kfree(rtl8139if, sizeof(rtl1839if_t));
return ERR_MEM;
}
memset(rtl8139if->rx_buffer, 0, 8192+16);
/* allocate the send buffers */
rtl8139if->tx_buffer[0] = mem_allocation(4*4096, MAP_KERNEL_SPACE|MAP_NO_CACHE);
if (!(rtl8139if->tx_buffer[0])) {
LWIP_DEBUGF(NETIF_DEBUG, ("rtl8139if_init: out of memory\n"));
kfree(rtl8139if->rx_buffer, 8192+16);
kfree(rtl8139if, sizeof(rtl1839if_t));
return ERR_MEM;
}
memset(rtl8139if->tx_buffer[0], 0, 4*4096);
rtl8139if->tx_buffer[1] = rtl8139if->tx_buffer[0] + 4096;
rtl8139if->tx_buffer[2] = rtl8139if->tx_buffer[1] + 4096;
rtl8139if->tx_buffer[3] = rtl8139if->tx_buffer[2] + 4096;
netif->state = rtl8139if;
mynetif = netif;
tmp8 = 0;
while (board_tbl[tmp8].vendor_str) {
if (pci_get_device_info(board_tbl[tmp8].vendor, board_tbl[tmp8].device,
&rtl8139if->iobase, &rtl8139if->irq) == 0)
break;
tmp8++;
}
if (!board_tbl[tmp8].vendor_str)
return ERR_ARG;
tmp32 = inportl(rtl8139if->iobase + TCR);
if (tmp32 == 0xFFFFFF) {
LWIP_DEBUGF(NETIF_DEBUG, ("rtl8139if_init: ERROR\n"));
return ERR_ARG;
}
// determine the hardware revision
//tmp32 = (tmp32 & TCR_HWVERID) >> TCR_HWOFFSET;
irq_install_handler(rtl8139if->irq+32, rtl8139if_handler);
LWIP_DEBUGF(NETIF_DEBUG, ("rtl8139if_init: found %s at iobase 0x%x (irq %u)\n", board_tbl[tmp8].device_str,
rtl8139if->iobase, rtl8139if->irq));
// determine the mac address of this card
LWIP_DEBUGF(NETIF_DEBUG, ("rtl8139if_init: MAC address "));
for (tmp8=0; tmp8<6; tmp8++) {
mynetif->hwaddr[tmp8] = inportb(rtl8139if->iobase + IDR0 + tmp8);
LWIP_DEBUGF(NETIF_DEBUG, ("%02x ", mynetif->hwaddr[tmp8]));
}
LWIP_DEBUGF(NETIF_DEBUG, ("\n"));
// Software reset
outportb(rtl8139if->iobase + CR, CR_RST);
/*
* 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.
*/
udelay(10000);
tmp16 = 10000;
while ((inportb(rtl8139if->iobase + CR) & CR_RST) && tmp16 > 0) {
tmp16--;
}
if (!tmp16) {
// it seems not to work
kprintf("RTL8139 reset failed\n");
return ERR_ARG;
}
// Enable Receive and Transmitter
outportb(rtl8139if->iobase + CR, CR_TE|CR_RE); // Sets the RE and TE bits high
// lock config register
outportb(rtl8139if->iobase + CR9346, CR9346_EEM1 | CR9346_EEM0);
// clear all of CONFIG1
outportb(rtl8139if->iobase + CONFIG1, 0);
// disable driver loaded and lanwake bits, turn driver loaded bit back on
outportb(rtl8139if->iobase + CONFIG1,
(inportb(rtl8139if->iobase + CONFIG1) & ~(CONFIG1_DVRLOAD | CONFIG1_LWACT)) | CONFIG1_DVRLOAD);
// unlock config register
outportb(rtl8139if->iobase + CR9346, 0);
/*
* configure receive buffer
* AB - Accept Broadcast: Accept broadcast packets sent to mac ff:ff:ff:ff:ff:ff
* AM - Accept Multicast: Accept multicast packets.
* APM - Accept Physical Match: Accept packets send to NIC's MAC address.
* AAP - Accept All Packets. Accept all packets (run in promiscuous mode).
*/
outportl(rtl8139if->iobase + RCR, RCR_MXDMA2|RCR_MXDMA1|RCR_MXDMA0|RCR_AB|RCR_AM|RCR_APM|RCR_AAP); // The WRAP bit isn't set
// set the transmit config register to
// be the normal interframe gap time
// set DMA max burst to 64bytes
outportl(rtl8139if->iobase + TCR, TCR_IFG|TCR_MXDMA0|TCR_MXDMA1|TCR_MXDMA2);
// register the receive buffer
outportl(rtl8139if->iobase + RBSTART, virt_to_phys((size_t) rtl8139if->rx_buffer));
// set each of the transmitter start address descriptors
outportl(rtl8139if->iobase + TSAD0, virt_to_phys((size_t) rtl8139if->tx_buffer[0]));
outportl(rtl8139if->iobase + TSAD1, virt_to_phys((size_t) rtl8139if->tx_buffer[1]));
outportl(rtl8139if->iobase + TSAD2, virt_to_phys((size_t) rtl8139if->tx_buffer[2]));
outportl(rtl8139if->iobase + TSAD3, virt_to_phys((size_t) rtl8139if->tx_buffer[3]));
/*
* To set the RTL8139 to accept only the Transmit OK (TOK) and Receive OK (ROK)
* interrupts, we would have the TOK and ROK bits of the IMR high and leave the
* rest low. That way when a TOK or ROK IRQ happens, it actually will go through
* and fire up an IRQ.
*/
outportw(rtl8139if->iobase + IMR, ISR_ROK|ISR_TOK|ISR_RXOVW|ISR_TER|ISR_RER);
outportw(rtl8139if->iobase + BMCR, BMCR_ANE);
tmp16 = inportw(rtl8139if->iobase + BMCR);
if (tmp16 & BMCR_SPD1000)
speed = 1000;
else if (tmp16 & BMCR_SPD100)
speed = 100;
else
speed = 10;
// Enable Receive and Transmitter
outportb(rtl8139if->iobase + CR, CR_TE|CR_RE); // Sets the RE and TE bits high
kprintf("RTL8139: CR = 0x%x, ISR = 0x%x, speed = %u mbps\n",
inportb(rtl8139if->iobase + CR), inportw(rtl8139if->iobase + ISR), speed);
/*
* Initialize the snmp variables and counters inside the struct netif.
* The last argument should be replaced with your link speed, in units
* of bits per second.
*/
NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, speed);
/* administrative details */
netif->name[0] = 'e';
netif->name[1] = 'n';
netif->num = num;
num++;
/* downward functions */
netif->output = etharp_output;
netif->linkoutput = rtl8139if_output;
/* maximum transfer unit */
netif->mtu = 1500;
/* broadcast capability */
netif->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
/* hardware address length */
netif->hwaddr_len = 6;
rtl8139if->ethaddr = (struct eth_addr *)netif->hwaddr;
return ERR_OK;
}
#endif