diff --git a/hermit/drivers/net/Makefile b/hermit/drivers/net/Makefile index 5dd32bed7..e7dbc74a4 100644 --- a/hermit/drivers/net/Makefile +++ b/hermit/drivers/net/Makefile @@ -1,4 +1,4 @@ -C_source := mmnif.c util.c +C_source := mmnif.c util.c e1000.c rtl8139.c MODULE := drivers_net include $(TOPDIR)/Makefile.inc diff --git a/hermit/drivers/net/e1000.c b/hermit/drivers/net/e1000.c new file mode 100644 index 000000000..0800ce923 --- /dev/null +++ b/hermit/drivers/net/e1000.c @@ -0,0 +1,596 @@ +/* + * Copyright 2012 Stefan Lankes, Chair for Operating Systems, + * RWTH Aachen University + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RX_BUF_LEN (2048) +#define TX_BUF_LEN (1792) + +#define INT_MASK (E1000_IMS_RXO|E1000_IMS_RXT0|E1000_IMS_RXDMT0|E1000_IMS_RXSEQ|E1000_IMS_LSC) +#define INT_MASK_NO_RX (E1000_IMS_LSC) + +typedef struct { + char *vendor_str; + char *device_str; + uint32_t vendor; + uint32_t device; +} board_t; + +static board_t board_tbl[] = +{ + {"Intel", "Intel E1000 (82542)", 0x8086, 0x1000}, + {"Intel", "Intel E1000 (82543GC FIBER)", 0x8086, 0x1001}, + {"Intel", "Intel E1000 (82543GC COPPER)", 0x8086, 0x1004}, + {"Intel", "Intel E1000 (82544EI COPPER)", 0x8086, 0x1008}, + {"Intel", "Intel E1000 (82544EI FIBER)", 0x8086, 0x1009}, + {"Intel", "Intel E1000 (82544GC COPPER)", 0x8086, 0x100C}, + {"Intel", "Intel E1000 (82544GC LOM)", 0x8086, 0x100D}, + {"Intel", "Intel E1000 (82540EM)", 0x8086, 0x100E}, + {"Intel", "Intel E1000 (82540EM LOM)", 0x8086, 0x1015}, + {"Intel", "Intel E1000 (82540EP LOM)", 0x8086, 0x1016}, + {"Intel", "Intel E1000 (82540EP)", 0x8086, 0x1017}, + {"Intel", "Intel E1000 (82540EP LP)", 0x8086, 0x101E}, + {"Intel", "Intel E1000 (82545EM COPPER)", 0x8086, 0x100F}, + {"Intel", "Intel E1000 (82545EM FIBER)", 0x8086, 0x1011}, + {"Intel", "Intel E1000 (82545GM COPPER)", 0x8086, 0x1026}, + {"Intel", "Intel E1000 (82545GM FIBER)", 0x8086, 0x1027}, + {"Intel", "Intel E1000 (82545GM SERDES)", 0x8086, 0x1028}, + {"Intel", "Intel E1000 (82546EB COPPER)", 0x8086, 0x1010}, + {"Intel", "Intel E1000 (82546EB FIBER)", 0x8086, 0x1012}, + {"Intel", "Intel E1000 (82546EB QUAD COPPER)", 0x8086, 0x101D}, + //{"Intel", "Intel E1000 (82541EI)", 0x8086, 0x1013}, + //{"Intel", "Intel E1000 (82541EI MOBILE)", 0x8086, 0x1018}, + //{"Intel", "Intel E1000 (82541ER LOM)", 0x8086, 0x1014}, + //{"Intel", "Intel E1000 (82541ER)", 0x8086, 0x1078}, + {"Intel", "Intel E1000 (82547GI)", 0x8086, 0x1075}, + {"Intel", "Intel E1000 (82541GI)", 0x8086, 0x1076}, + {"Intel", "Intel E1000 (82541GI MOBILE)", 0x8086, 0x1077}, + {"Intel", "Intel E1000 (82541GI LF)", 0x8086, 0x107C}, + {"Intel", "Intel E1000 (82546GB COPPER)", 0x8086, 0x1079}, + {"Intel", "Intel E1000 (82546GB FIBER)", 0x8086, 0x107A}, + {"Intel", "Intel E1000 (82546GB SERDES)", 0x8086, 0x107B}, + {"Intel", "Intel E1000 (82546GB PCIE)", 0x8086, 0x108A}, + {"Intel", "Intel E1000 (82546GB QUAD COPPER)", 0x8086, 0x1099}, + //{"Intel", "Intel E1000 (82547EI)", 0x8086, 0x1019}, + //{"Intel", "Intel E1000 (82547EI_MOBILE)", 0x8086, 0x101A}, + {"Intel", "Intel E1000 (82546GB QUAD COPPER KSP3)", 0x8086, 0x10B5}, + {NULL,}, +}; + +static struct netif* mynetif = NULL; + +static inline uint32_t e1000_read(volatile uint8_t* base, uint32_t off) +{ +#if 1 + uint32_t ret; + + asm volatile ("movl (%1), %0" : "=r"(ret) : "r"(base+off)); + + return ret; +#else + return *((volatile uint32_t*) (base+off)); +#endif +} + +static inline void e1000_write(volatile uint8_t* base, uint32_t off, uint32_t value) +{ + *((volatile uint32_t*) (base+off)) = value; +} + +static inline void e1000_flush(volatile uint8_t* base) +{ + e1000_read(base, E1000_STATUS); +} + +#if 1 +static uint16_t eeprom_read(volatile uint8_t* base, uint8_t addr) +{ + uint16_t data; + uint32_t tmp; + + e1000_write(base, E1000_EERD, 1 | ((uint32_t)(addr) << 8)); + + while(!((tmp = e1000_read(base, E1000_EERD)) & (1 << 4))) + udelay(1); + + data = (uint16_t)((tmp >> 16) & 0xFFFF); + + return data; +} +#else +// Only for 82541xx and 82547GI/EI +static uint16_t eeprom_read(uint8_t* base, uint8_t addr) +{ + uint16_t data; + uint32_t tmp; + + e1000_write(base, E1000_EERD, 1 | ((uint32_t)(addr) << 2)); + + while(!((tmp = e1000_read(base, E1000_EERD)) & (1 << 1))) + udelay(1); + + data = (uint16_t)((tmp >> 16) & 0xFFFF); + + return data; +} +#endif + +/* + * @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 e1000if_output(struct netif* netif, struct pbuf* p) +{ + e1000if_t* e1000if = netif->state; + uint32_t i; + struct pbuf *q; + + if (BUILTIN_EXPECT((p->tot_len > 1792) || (p->tot_len > TX_BUF_LEN), 0)) { + LWIP_DEBUGF(NETIF_DEBUG, ("e1000if_output: packet is longer than 1792 bytes\n")); + return ERR_IF; + } + + if (!(e1000if->tx_desc[e1000if->tx_tail].status & 0xF)) { + LWIP_DEBUGF(NETIF_DEBUG, ("e1000if_output: %i already inuse\n", e1000if->tx_tail)); + return ERR_IF; + } + +#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((void*) ((size_t) e1000if->tx_buffers + e1000if->tx_tail*TX_BUF_LEN + i), q->payload, q->len); + i += q->len; + } + + e1000if->tx_desc[e1000if->tx_tail].length = p->tot_len; + e1000if->tx_desc[e1000if->tx_tail].status = 0; + e1000if->tx_desc[e1000if->tx_tail].cmd = (1 << 3) | 3; + + // update the tail so the hardware knows it's ready + e1000if->tx_tail = (e1000if->tx_tail + 1) % NUM_TX_DESCRIPTORS; + e1000_write(e1000if->bar0, E1000_TDT, e1000if->tx_tail); + +#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 e1000_rx_inthandler(struct netif* netif) +{ + e1000if_t* e1000if = netif->state; + struct pbuf *p = NULL; + struct pbuf* q; + uint16_t length, i; + + while(e1000if->rx_desc[e1000if->rx_tail].status & (1 << 0)) + { + if (!(e1000if->rx_desc[e1000if->rx_tail].status & (1 << 1))) { + LINK_STATS_INC(link.drop); + goto no_eop; // currently, we ignore packets without EOP flag + } + + length = e1000if->rx_desc[e1000if->rx_tail].length; + + if (!e1000if->rx_desc[e1000if->rx_tail].errors) { +#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, i=0; q!=NULL; q=q->next) { + memcpy((uint8_t*) q->payload, (void*) ((size_t) e1000if->rx_buffers + e1000if->rx_tail*RX_BUF_LEN + i), q->len); + i += q->len; + } +#if ETH_PAD_SIZE + pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */ +#endif + LINK_STATS_INC(link.recv); + + // forward packet to LwIP + netif->input(p, netif); + } else { + LWIP_DEBUGF(NETIF_DEBUG, ("e1000if_rx_inthandler: not enough memory!\n")); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + } + } else { + LWIP_DEBUGF(NETIF_DEBUG, ("e1000if_rx_inthandler: RX errors (0x%x)\n", e1000if->rx_desc[e1000if->rx_tail].errors)); + LINK_STATS_INC(link.drop); + } + +no_eop: + e1000if->rx_desc[e1000if->rx_tail].status = 0; + + // update tail and write the value to the device + e1000if->rx_tail = (e1000if->rx_tail + 1) % NUM_RX_DESCRIPTORS; + e1000_write(e1000if->bar0, E1000_RDT, e1000if->rx_tail); + } + + e1000if->polling = 0; + // enable all known interrupts + e1000_write(e1000if->bar0, E1000_IMS, INT_MASK); + e1000_flush(e1000if->bar0); +} + +/* this function is called in the context of the tcpip thread or the irq handler (by using NO_SYS) */ +static void e1000if_poll(void* ctx) +{ + e1000_rx_inthandler(mynetif); +} + +static void e1000if_handler(struct state* s) +{ + e1000if_t* e1000if = mynetif->state; + uint32_t icr; + + // disable all interrupts + e1000_write(e1000if->bar0, E1000_IMC, INT_MASK|0xFFFE0000); + e1000_flush(e1000if->bar0); + + // read the pending interrupt status + icr = e1000_read(e1000if->bar0, E1000_ICR); + + // ignore tx success stuff + icr &= ~3; + + // LINK STATUS CHANGE + if (icr & E1000_ICR_LSC) + { + icr &= ~E1000_ICR_LSC; + LWIP_DEBUGF(NETIF_DEBUG, ("e1000if: Link status change (TODO)\n")); + } + + if (icr & (E1000_ICR_RXT0|E1000_ICR_RXDMT0|E1000_ICR_RXO)) { + icr &= ~(E1000_ICR_RXT0|E1000_ICR_RXDMT0|E1000_ICR_RXO); + + if (!e1000if->polling) { +#if NO_SYS + e1000if_poll(NULL); +#else + if (tcpip_callback_with_block(e1000if_poll, NULL, 0) == ERR_OK) { + e1000if->polling = 1; + } else { + LWIP_DEBUGF(NETIF_DEBUG, ("e1000if_handler: unable to send a poll request to the tcpip thread\n")); + } +#endif + } + } + + if (e1000if->polling) // now, the tcpip thread will check for incoming messages + e1000_write(e1000if->bar0, E1000_IMS, INT_MASK_NO_RX); + else + e1000_write(e1000if->bar0, E1000_IMS, INT_MASK); // enable interrupts + e1000_flush(e1000if->bar0); + + if (icr & 0x1FFFF) { + LWIP_DEBUGF(NETIF_DEBUG, ("e1000if_handler: unhandled interrupt #%u received! (0x%x)\n", e1000if->irq, icr)); + } +} + +err_t e1000if_init(struct netif* netif) +{ + pci_info_t pci_info; + e1000if_t* e1000if = NULL; + uint32_t tmp32; + uint16_t tmp16, speed, cold = 0x40; + uint8_t tmp8, is64bit, mem_type, prefetch; + static uint8_t num = 0; + + LWIP_ASSERT("netif != NULL", (netif != NULL)); + + tmp8 = 0; + while (board_tbl[tmp8].vendor_str) { + if (pci_get_device_info(board_tbl[tmp8].vendor, board_tbl[tmp8].device, &pci_info) == 0) + break; + tmp8++; + } + + if (!board_tbl[tmp8].vendor_str) + return ERR_ARG; + + mem_type = pci_info.base[0] & 0x1; + is64bit = pci_info.base[0] & 0x6 ? 1 : 0; + prefetch = pci_info.base[0] & 0x8 ? 1 : 0; + + if (mem_type) { + LWIP_DEBUGF(NETIF_DEBUG, ("e1000if_init: IO space is currently not supported!\n")); + return ERR_ARG; + } + + if (is64bit) { + LWIP_DEBUGF(NETIF_DEBUG, ("e1000if_init: 64bit mode is currently not supported!\n")); + return ERR_ARG; + } + + e1000if = kmalloc(sizeof(e1000if_t)); + if (!e1000if) { + LWIP_DEBUGF(NETIF_DEBUG, ("e1000if_init: out of memory\n")); + return ERR_MEM; + } + memset(e1000if, 0x00, sizeof(e1000if_t)); + + netif->state = e1000if; + mynetif = netif; + + e1000if->bar0 = (uint8_t*) vma_alloc(PAGE_FLOOR(pci_info.size[0]), VMA_READ|VMA_WRITE); + if (BUILTIN_EXPECT(!e1000if->bar0, 0)) + goto oom; + + int ret = page_map((size_t)e1000if->bar0, PAGE_CEIL(pci_info.base[0]), PAGE_FLOOR(pci_info.size[0]) >> PAGE_BITS, PG_GLOBAL|PG_RW|PG_PCD); + if (BUILTIN_EXPECT(ret, 0)) + goto oom; + + // reset device + e1000_write(e1000if->bar0, E1000_CTRL, E1000_CTRL_RST); + e1000_flush(e1000if->bar0); + /* Wait for reset to complete */ + udelay(10); + + e1000if->irq = pci_info.irq; + e1000if->rx_desc = page_alloc(NUM_RX_DESCRIPTORS*sizeof(rx_desc_t), VMA_READ|VMA_WRITE); + if (BUILTIN_EXPECT(!e1000if->rx_desc, 0)) + goto oom; + memset((void*) e1000if->rx_desc, 0x00, NUM_RX_DESCRIPTORS*sizeof(rx_desc_t)); + e1000if->tx_desc = page_alloc(NUM_TX_DESCRIPTORS*sizeof(tx_desc_t), VMA_READ|VMA_WRITE); + if (BUILTIN_EXPECT(!e1000if->tx_desc, 0)) + goto oom; + memset((void*) e1000if->tx_desc, 0x00, NUM_TX_DESCRIPTORS*sizeof(tx_desc_t)); + + LWIP_DEBUGF(NETIF_DEBUG, ("e1000if_init: Found %s at mmio 0x%x (size 0x%x), irq %u\n", board_tbl[tmp8].device_str, + pci_info.base[0] & ~0xF, pci_info.size[0], e1000if->irq)); + //LWIP_DEBUGF(NETIF_DEBUG, ("e1000if_init: Map iobase to %p\n", e1000if->bar0)); + LWIP_DEBUGF(NETIF_DEBUG, ("e1000if_init: is64bit %u, prefetch %u\n", is64bit, prefetch)); + + /* hardware address length */ + netif->hwaddr_len = ETHARP_HWADDR_LEN; + + // determine the mac address of this card + for (tmp8=0; tmp8bar0, tmp8 / 2); + netif->hwaddr[tmp8] = (tmp16 & 0xFF); + netif->hwaddr[tmp8+1] = (tmp16 >> 8) & 0xFF; + } + + e1000if->tx_buffers = page_alloc(NUM_TX_DESCRIPTORS*TX_BUF_LEN, VMA_READ|VMA_WRITE); + if (BUILTIN_EXPECT(!e1000if->tx_buffers, 0)) + goto oom; + memset((void*) e1000if->tx_buffers, 0x00, NUM_TX_DESCRIPTORS*TX_BUF_LEN); + for(tmp32=0; tmp32 < NUM_TX_DESCRIPTORS; tmp32++) { + e1000if->tx_desc[tmp32].addr = virt_to_phys((size_t)e1000if->tx_buffers + tmp32*TX_BUF_LEN); + e1000if->tx_desc[tmp32].status = 1; + } + + //LWIP_DEBUGF(NETIF_DEBUG, ("e1000if_init: add TX ring buffer %p (viraddr %p)\n", virt_to_phys((size_t)e1000if->tx_desc), e1000if->tx_desc)); + + /* General configuration */ + tmp32 = e1000_read(e1000if->bar0, E1000_CTRL); + tmp32 &= ~(E1000_CTRL_VME|E1000_CTRL_FD|E1000_CTRL_ILOS|E1000_CTRL_PHY_RST|E1000_CTRL_LRST|E1000_CTRL_FRCSPD); + e1000_write(e1000if->bar0, E1000_CTRL, tmp32 | E1000_CTRL_SLU | E1000_CTRL_ASDE); + e1000_flush(e1000if->bar0); + kprintf("e1000if_init: Device Control Register 0x%x\n", e1000_read(e1000if->bar0, E1000_CTRL)); + + /* make sure transmits are disabled while setting up the descriptors */ + tmp32 = e1000_read(e1000if->bar0, E1000_TCTL); + e1000_write(e1000if->bar0, E1000_TCTL, tmp32 & ~E1000_TCTL_EN); + e1000_flush(e1000if->bar0); + + // setup the transmit descriptor ring buffer + e1000_write(e1000if->bar0, E1000_TDBAL, (uint32_t)((uint64_t)virt_to_phys((size_t)e1000if->tx_desc) & 0xFFFFFFFF)); + e1000_write(e1000if->bar0, E1000_TDBAH, (uint32_t)((uint64_t)virt_to_phys((size_t)e1000if->tx_desc) >> 32)); + //LWIP_DEBUGF(NETIF_DEBUG, ("e1000if_init: TDBAH/TDBAL = 0x%x:0x%x\n", e1000_read(e1000if->bar0, E1000_TDBAH), e1000_read(e1000if->bar0, E1000_TDBAL))); + + // transmit buffer length; NUM_TX_DESCRIPTORS 16-byte descriptors + e1000_write(e1000if->bar0, E1000_TDLEN , (uint32_t)(NUM_TX_DESCRIPTORS * sizeof(tx_desc_t))); + + // setup head and tail pointers + e1000_write(e1000if->bar0, E1000_TDH, 0); + e1000_write(e1000if->bar0, E1000_TDT, 0); + e1000if->tx_tail = 0; + + tmp32 = e1000_read(e1000if->bar0, E1000_STATUS); + if (tmp32 & E1000_STATUS_SPEED_1000) + speed = 1000; + else if (tmp32 & E1000_STATUS_SPEED_100) + speed = 100; + else + speed = 10; + + if ((!(tmp32 & E1000_STATUS_FD)) && (speed == 1000)) + cold = 0x200; + LWIP_DEBUGF(NETIF_DEBUG, ("e1000if_init: speed = %u mbps\n", speed)); + LWIP_DEBUGF(NETIF_DEBUG, ("e1000if_init: Full-Duplex %u\n", tmp32 & E1000_STATUS_FD)); + + // set the transmit control register (padshortpackets) + e1000_write(e1000if->bar0, E1000_TCTL, (E1000_TCTL_EN | E1000_TCTL_PSP | (cold << 12) | (0x10 << 4))); + e1000_flush(e1000if->bar0); + + // set IEEE 802.3 standard IPG value + e1000_write(e1000if->bar0, E1000_TIPG, (6 << 20) | (8 << 10) | 10); + + // set MAC address + for(tmp8=0; tmp8<4; tmp8++) + ((uint8_t*) &tmp32)[tmp8] = netif->hwaddr[tmp8]; + e1000_write(e1000if->bar0, E1000_RA, tmp32); + tmp32 = 0; + for(tmp8=0; tmp8<2; tmp8++) + ((uint8_t*) &tmp32)[tmp8] = netif->hwaddr[tmp8+4]; + e1000_write(e1000if->bar0, E1000_RA+4, tmp32 | (1 << 31)); // set also AV bit to check incoming packets + + /* Zero out the other receive addresses. */ + for (tmp8=1; tmp8<16; tmp8++) { + e1000_write(e1000if->bar0, E1000_RA+8*tmp8, 0); + e1000_write(e1000if->bar0, E1000_RA+8*tmp8+4, 0); + } + + LWIP_DEBUGF(NETIF_DEBUG, ("e1000if_init: MAC address ")); + tmp32 = e1000_read(e1000if->bar0, E1000_RA); + for(tmp8=0; tmp8<4; tmp8++) { + LWIP_DEBUGF(NETIF_DEBUG, ("%02x ", ((uint8_t*) &tmp32)[tmp8])); + } + tmp32 = e1000_read(e1000if->bar0, E1000_RA+4); + for(tmp8=0; tmp8<2; tmp8++) { + LWIP_DEBUGF(NETIF_DEBUG, ("%02x ", ((uint8_t*) &tmp32)[tmp8])); + } + LWIP_DEBUGF(NETIF_DEBUG, ("\n")); + e1000_flush(e1000if->bar0); + + // set multicast table to 0 + for(tmp8=0; tmp8<128; tmp8++ ) + e1000_write(e1000if->bar0, E1000_MTA + (tmp8 * 4), 0); + e1000_flush(e1000if->bar0); + + // set IRQ handler + irq_install_handler(e1000if->irq+32, e1000if_handler); + + /* make sure receives are disabled while setting up the descriptors */ + tmp32 = e1000_read(e1000if->bar0, E1000_RCTL); + e1000_write(e1000if->bar0, E1000_RCTL, tmp32 & ~E1000_RCTL_EN); + e1000_flush(e1000if->bar0); + + // clear IMS & IMC registers + e1000_write(e1000if->bar0, E1000_IMS, 0xFFFF); + e1000_flush(e1000if->bar0); + e1000_write(e1000if->bar0, E1000_IMC, 0xFFFF); + e1000_flush(e1000if->bar0); + + // enable all interrupts (and clear existing pending ones) + e1000_write(e1000if->bar0, E1000_IMS, INT_MASK); + e1000_flush(e1000if->bar0); + e1000_read(e1000if->bar0, E1000_ICR); + + LWIP_DEBUGF(NETIF_DEBUG, ("e1000if_init: Interrupt Mask is set to 0x%x\n", e1000_read(e1000if->bar0, E1000_IMS))); + + //LWIP_DEBUGF(NETIF_DEBUG, ("e1000if_init: add RX ring buffer %p (viraddr %p)\n", virt_to_phys((size_t)e1000if->rx_desc), e1000if->rx_desc)); + + e1000if->rx_buffers = page_alloc(NUM_RX_DESCRIPTORS*RX_BUF_LEN, VMA_READ|VMA_WRITE); + if (BUILTIN_EXPECT(!e1000if->rx_buffers, 0)) + goto oom; + memset(e1000if->rx_buffers, 0x00, NUM_RX_DESCRIPTORS*RX_BUF_LEN); + for(tmp32=0; tmp32 < NUM_RX_DESCRIPTORS; tmp32++) + e1000if->rx_desc[tmp32].addr = virt_to_phys((size_t)e1000if->rx_buffers + tmp32*RX_BUF_LEN); + + // setup the receive descriptor ring buffer + e1000_write(e1000if->bar0, E1000_RDBAH, (uint32_t)((uint64_t)virt_to_phys((size_t)e1000if->rx_desc) >> 32)); + e1000_write(e1000if->bar0, E1000_RDBAL, (uint32_t)((uint64_t)virt_to_phys((size_t)e1000if->rx_desc) & 0xFFFFFFFF)); + + // receive buffer length; NUM_RX_DESCRIPTORS 16-byte descriptors + e1000_write(e1000if->bar0, E1000_RDLEN , (uint32_t)(NUM_RX_DESCRIPTORS * sizeof(rx_desc_t))); + + // setup head and tail pointers + e1000_write(e1000if->bar0, E1000_RDH, 0); + e1000_write(e1000if->bar0, E1000_RDT, 0); + e1000if->rx_tail = 0; + + // set the receieve control register + e1000_write(e1000if->bar0, E1000_RCTL, (E1000_RCTL_EN|/*E1000_RCTL_LPE|*/E1000_RCTL_LBM_NO|E1000_RCTL_BAM|E1000_RCTL_SZ_2048| + E1000_RCTL_SECRC|E1000_RCTL_RDMTS_HALF|E1000_RCTL_MO_0/*|E1000_RCTL_UPE|E1000_RCTL_MPE*/)); + e1000_flush(e1000if->bar0); + + LWIP_DEBUGF(NETIF_DEBUG, ("e1000if_init: status = 0x%x\n", e1000_read(e1000if->bar0, E1000_STATUS))); + + /* + * 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 = e1000if_output; + /* maximum transfer unit */ + netif->mtu = 1500; + /* broadcast capability */ + netif->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_IGMP; + + e1000if->ethaddr = (struct eth_addr *)netif->hwaddr; + + return ERR_OK; + +oom: + if (e1000if) + { + if (e1000if->rx_desc) + page_free((void*) e1000if->rx_desc, NUM_RX_DESCRIPTORS*sizeof(rx_desc_t)); + if (e1000if->tx_desc) + page_free((void*) e1000if->tx_desc, NUM_TX_DESCRIPTORS*sizeof(tx_desc_t)); + if (e1000if->tx_buffers) + page_free(e1000if->tx_buffers, NUM_TX_DESCRIPTORS*TX_BUF_LEN); + if (e1000if->rx_buffers) + page_free(e1000if->rx_buffers, NUM_RX_DESCRIPTORS*RX_BUF_LEN); + if (e1000if->bar0) { + e1000_write(e1000if->bar0, E1000_CTRL, E1000_CTRL_RST); + + // TODO: unmap e1000if->bar0 + } + + irq_uninstall_handler(e1000if->irq+32); + + kfree(e1000if); + } + + memset(netif, 0x00, sizeof(struct netif)); + mynetif = NULL; + + return ERR_MEM; +} diff --git a/hermit/drivers/net/e1000.h b/hermit/drivers/net/e1000.h new file mode 100644 index 000000000..08f0d886f --- /dev/null +++ b/hermit/drivers/net/e1000.h @@ -0,0 +1,326 @@ +/* + * Copyright 2012 Stefan Lankes, Chair for Operating Systems, + * RWTH Aachen University + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __NET_E1000_H__ +#define __NET_E1000_H__ + +#include +#include + +#define NUM_RX_DESCRIPTORS 64 +#define NUM_TX_DESCRIPTORS 64 + +#define E1000_CTRL 0x00000 /* Device Control - RW */ +#define E1000_CTRL_DUP 0x00004 /* Device Control Duplicate (Shadow) - RW */ +#define E1000_STATUS 0x00008 /* Device Status - RO */ +#define E1000_EECD 0x00010 /* EEPROM/Flash Control - RW */ +#define E1000_EERD 0x00014 /* EEPROM Read - RW */ +#define E1000_CTRL_EXT 0x00018 /* Extended Device Control - RW */ +#define E1000_ICR 0x000C0 /* Interrupt Cause Read - R/clr */ +#define E1000_ITR 0x000C4 /* Interrupt Throttling Rate - RW */ +#define E1000_ICS 0x000C8 /* Interrupt Cause Set - WO */ +#define E1000_IMS 0x000D0 /* Interrupt Mask Set - RW */ +#define E1000_IMC 0x000D8 /* Interrupt Mask Clear - WO */ +#define E1000_IAM 0x000E0 /* Interrupt Acknowledge Auto Mask */ +#define E1000_RCTL 0x00100 /* RX Control - RW */ +#define E1000_TCTL 0x00400 /* TX Control - RW */ +#define E1000_TIPG 0x00410 /* TX Inter-packet gap -RW */ +#define E1000_RDBAL 0x02800 /* RX Descriptor Base Address Low - RW */ +#define E1000_RDBAH 0x02804 /* RX Descriptor Base Address High - RW */ +#define E1000_RDLEN 0x02808 /* RX Descriptor Length - RW */ +#define E1000_RDH 0x02810 /* RX Descriptor Head - RW */ +#define E1000_RDT 0x02818 /* RX Descriptor Tail - RW */ +#define E1000_TDBAL 0x03800 /* TX Descriptor Base Address Low - RW */ +#define E1000_TDBAH 0x03804 /* TX Descriptor Base Address High - RW */ +#define E1000_TDLEN 0x03808 /* TX Descriptor Length - RW */ +#define E1000_TDH 0x03810 /* TX Descriptor Head - RW */ +#define E1000_TDT 0x03818 /* TX Descripotr Tail - RW */ +#define E1000_MTA 0x05200 /* Multicast Table Array - RW Array */ +#define E1000_RA 0x05400 /* Receive Address - RW Array */ + +/* Device Control */ +#define E1000_CTRL_FD 0x00000001 /* Full duplex.0=half; 1=full */ +#define E1000_CTRL_BEM 0x00000002 /* Endian Mode.0=little,1=big */ +#define E1000_CTRL_PRIOR 0x00000004 /* Priority on PCI. 0=rx,1=fair */ +#define E1000_CTRL_GIO_MASTER_DISABLE 0x00000004 /*Blocks new Master requests */ +#define E1000_CTRL_LRST 0x00000008 /* Link reset. 0=normal,1=reset */ +#define E1000_CTRL_TME 0x00000010 /* Test mode. 0=normal,1=test */ +#define E1000_CTRL_SLE 0x00000020 /* Serial Link on 0=dis,1=en */ +#define E1000_CTRL_ASDE 0x00000020 /* Auto-speed detect enable */ +#define E1000_CTRL_SLU 0x00000040 /* Set link up (Force Link) */ +#define E1000_CTRL_ILOS 0x00000080 /* Invert Loss-Of Signal */ +#define E1000_CTRL_SPD_SEL 0x00000300 /* Speed Select Mask */ +#define E1000_CTRL_SPD_10 0x00000000 /* Force 10Mb */ +#define E1000_CTRL_SPD_100 0x00000100 /* Force 100Mb */ +#define E1000_CTRL_SPD_1000 0x00000200 /* Force 1Gb */ +#define E1000_CTRL_BEM32 0x00000400 /* Big Endian 32 mode */ +#define E1000_CTRL_FRCSPD 0x00000800 /* Force Speed */ +#define E1000_CTRL_FRCDPX 0x00001000 /* Force Duplex */ +#define E1000_CTRL_D_UD_EN 0x00002000 /* Dock/Undock enable */ +#define E1000_CTRL_D_UD_POLARITY 0x00004000 /* Defined polarity of Dock/Undock indication in SDP[0] */ +#define E1000_CTRL_FORCE_PHY_RESET 0x00008000 /* Reset both PHY ports, through PHYRST_N pin */ +#define E1000_CTRL_EXT_LINK_EN 0x00010000 /* enable link status from external LINK_0 and LINK_1 pins */ +#define E1000_CTRL_SWDPIN0 0x00040000 /* SWDPIN 0 value */ +#define E1000_CTRL_SWDPIN1 0x00080000 /* SWDPIN 1 value */ +#define E1000_CTRL_SWDPIN2 0x00100000 /* SWDPIN 2 value */ +#define E1000_CTRL_SWDPIN3 0x00200000 /* SWDPIN 3 value */ +#define E1000_CTRL_SWDPIO0 0x00400000 /* SWDPIN 0 Input or output */ +#define E1000_CTRL_SWDPIO1 0x00800000 /* SWDPIN 1 input or output */ +#define E1000_CTRL_SWDPIO2 0x01000000 /* SWDPIN 2 input or output */ +#define E1000_CTRL_SWDPIO3 0x02000000 /* SWDPIN 3 input or output */ +#define E1000_CTRL_RST 0x04000000 /* Global reset */ +#define E1000_CTRL_RFCE 0x08000000 /* Receive Flow Control enable */ +#define E1000_CTRL_TFCE 0x10000000 /* Transmit flow control enable */ +#define E1000_CTRL_RTE 0x20000000 /* Routing tag enable */ +#define E1000_CTRL_VME 0x40000000 /* IEEE VLAN mode enable */ +#define E1000_CTRL_PHY_RST 0x80000000 /* PHY Reset */ +#define E1000_CTRL_SW2FW_INT 0x02000000 /* Initiate an interrupt to manageability engine */ + +/* Device Status */ +#define E1000_STATUS_FD 0x00000001 /* Full duplex.0=half,1=full */ +#define E1000_STATUS_LU 0x00000002 /* Link up.0=no,1=link */ +#define E1000_STATUS_FUNC_MASK 0x0000000C /* PCI Function Mask */ +#define E1000_STATUS_FUNC_SHIFT 2 +#define E1000_STATUS_FUNC_0 0x00000000 /* Function 0 */ +#define E1000_STATUS_FUNC_1 0x00000004 /* Function 1 */ +#define E1000_STATUS_TXOFF 0x00000010 /* transmission paused */ +#define E1000_STATUS_TBIMODE 0x00000020 /* TBI mode */ +#define E1000_STATUS_SPEED_MASK 0x000000C0 +#define E1000_STATUS_SPEED_10 0x00000000 /* Speed 10Mb/s */ +#define E1000_STATUS_SPEED_100 0x00000040 /* Speed 100Mb/s */ +#define E1000_STATUS_SPEED_1000 0x00000080 /* Speed 1000Mb/s */ +#define E1000_STATUS_LAN_INIT_DONE 0x00000200 /* Lan Init Completion + by EEPROM/Flash */ +#define E1000_STATUS_ASDV 0x00000300 /* Auto speed detect value */ +#define E1000_STATUS_DOCK_CI 0x00000800 /* Change in Dock/Undock state. Clear on write '0'. */ +#define E1000_STATUS_GIO_MASTER_ENABLE 0x00080000 /* Status of Master requests. */ +#define E1000_STATUS_MTXCKOK 0x00000400 /* MTX clock running OK */ +#define E1000_STATUS_PCI66 0x00000800 /* In 66Mhz slot */ +#define E1000_STATUS_BUS64 0x00001000 /* In 64 bit slot */ +#define E1000_STATUS_PCIX_MODE 0x00002000 /* PCI-X mode */ +#define E1000_STATUS_PCIX_SPEED 0x0000C000 /* PCI-X bus speed */ +#define E1000_STATUS_BMC_SKU_0 0x00100000 /* BMC USB redirect disabled */ +#define E1000_STATUS_BMC_SKU_1 0x00200000 /* BMC SRAM disabled */ +#define E1000_STATUS_BMC_SKU_2 0x00400000 /* BMC SDRAM disabled */ +#define E1000_STATUS_BMC_CRYPTO 0x00800000 /* BMC crypto disabled */ +#define E1000_STATUS_BMC_LITE 0x01000000 /* BMC external code execution disabled */ +#define E1000_STATUS_RGMII_ENABLE 0x02000000 /* RGMII disabled */ +#define E1000_STATUS_FUSE_8 0x04000000 +#define E1000_STATUS_FUSE_9 0x08000000 +#define E1000_STATUS_SERDES0_DIS 0x10000000 /* SERDES disabled on port 0 */ +#define E1000_STATUS_SERDES1_DIS 0x20000000 /* SERDES disabled on port 1 */ + +/* Transmit Control */ +#define E1000_TCTL_RST 0x00000001 /* software reset */ +#define E1000_TCTL_EN 0x00000002 /* enable tx */ +#define E1000_TCTL_BCE 0x00000004 /* busy check enable */ +#define E1000_TCTL_PSP 0x00000008 /* pad short packets */ +#define E1000_TCTL_CT 0x00000ff0 /* collision threshold */ +#define E1000_TCTL_COLD 0x003ff000 /* collision distance */ +#define E1000_TCTL_SWXOFF 0x00400000 /* SW Xoff transmission */ +#define E1000_TCTL_PBE 0x00800000 /* Packet Burst Enable */ +#define E1000_TCTL_RTLC 0x01000000 /* Re-transmit on late collision */ +#define E1000_TCTL_NRTU 0x02000000 /* No Re-transmit on underrun */ +#define E1000_TCTL_MULR 0x10000000 /* Multiple request support */ + +/* Receive Control */ +#define E1000_RCTL_RST 0x00000001 /* Software reset */ +#define E1000_RCTL_EN 0x00000002 /* enable */ +#define E1000_RCTL_SBP 0x00000004 /* store bad packet */ +#define E1000_RCTL_UPE 0x00000008 /* unicast promiscuous enable */ +#define E1000_RCTL_MPE 0x00000010 /* multicast promiscuous enable */ +#define E1000_RCTL_LPE 0x00000020 /* long packet enable */ +#define E1000_RCTL_LBM_NO 0x00000000 /* no loopback mode */ +#define E1000_RCTL_LBM_MAC 0x00000040 /* MAC loopback mode */ +#define E1000_RCTL_LBM_SLP 0x00000080 /* serial link loopback mode */ +#define E1000_RCTL_LBM_TCVR 0x000000C0 /* tcvr loopback mode */ +#define E1000_RCTL_DTYP_MASK 0x00000C00 /* Descriptor type mask */ +#define E1000_RCTL_DTYP_PS 0x00000400 /* Packet Split descriptor */ +#define E1000_RCTL_RDMTS_HALF 0x00000000 /* rx desc min threshold size */ +#define E1000_RCTL_RDMTS_QUAT 0x00000100 /* rx desc min threshold size */ +#define E1000_RCTL_RDMTS_EIGTH 0x00000200 /* rx desc min threshold size */ +#define E1000_RCTL_MO_SHIFT 12 /* multicast offset shift */ +#define E1000_RCTL_MO_0 0x00000000 /* multicast offset 11:0 */ +#define E1000_RCTL_MO_1 0x00001000 /* multicast offset 12:1 */ +#define E1000_RCTL_MO_2 0x00002000 /* multicast offset 13:2 */ +#define E1000_RCTL_MO_3 0x00003000 /* multicast offset 15:4 */ +#define E1000_RCTL_MDR 0x00004000 /* multicast desc ring 0 */ +#define E1000_RCTL_BAM 0x00008000 /* broadcast enable */ +/* these buffer sizes are valid if E1000_RCTL_BSEX is 0 */ +#define E1000_RCTL_SZ_2048 0x00000000 /* rx buffer size 2048 */ +#define E1000_RCTL_SZ_1024 0x00010000 /* rx buffer size 1024 */ +#define E1000_RCTL_SZ_512 0x00020000 /* rx buffer size 512 */ +#define E1000_RCTL_SZ_256 0x00030000 /* rx buffer size 256 */ +/* these buffer sizes are valid if E1000_RCTL_BSEX is 1 */ +#define E1000_RCTL_SZ_16384 0x00010000 /* rx buffer size 16384 */ +#define E1000_RCTL_SZ_8192 0x00020000 /* rx buffer size 8192 */ +#define E1000_RCTL_SZ_4096 0x00030000 /* rx buffer size 4096 */ +#define E1000_RCTL_VFE 0x00040000 /* vlan filter enable */ +#define E1000_RCTL_CFIEN 0x00080000 /* canonical form enable */ +#define E1000_RCTL_CFI 0x00100000 /* canonical form indicator */ +#define E1000_RCTL_DPF 0x00400000 /* discard pause frames */ +#define E1000_RCTL_PMCF 0x00800000 /* pass MAC control frames */ +#define E1000_RCTL_BSEX 0x02000000 /* Buffer size extension */ +#define E1000_RCTL_SECRC 0x04000000 /* Strip Ethernet CRC */ +#define E1000_RCTL_FLXBUF_MASK 0x78000000 /* Flexible buffer size */ +#define E1000_RCTL_FLXBUF_SHIFT 27 /* Flexible buffer shift */ + +/* Interrupt Cause Read */ +#define E1000_ICR_TXDW 0x00000001 /* Transmit desc written back */ +#define E1000_ICR_TXQE 0x00000002 /* Transmit Queue empty */ +#define E1000_ICR_LSC 0x00000004 /* Link Status Change */ +#define E1000_ICR_RXSEQ 0x00000008 /* rx sequence error */ +#define E1000_ICR_RXDMT0 0x00000010 /* rx desc min. threshold (0) */ +#define E1000_ICR_RXO 0x00000040 /* rx overrun */ +#define E1000_ICR_RXT0 0x00000080 /* rx timer intr (ring 0) */ +#define E1000_ICR_MDAC 0x00000200 /* MDIO access complete */ +#define E1000_ICR_RXCFG 0x00000400 /* RX /c/ ordered set */ +#define E1000_ICR_GPI_EN0 0x00000800 /* GP Int 0 */ +#define E1000_ICR_GPI_EN1 0x00001000 /* GP Int 1 */ +#define E1000_ICR_GPI_EN2 0x00002000 /* GP Int 2 */ +#define E1000_ICR_GPI_EN3 0x00004000 /* GP Int 3 */ +#define E1000_ICR_TXD_LOW 0x00008000 +#define E1000_ICR_SRPD 0x00010000 +#define E1000_ICR_ACK 0x00020000 /* Receive Ack frame */ +#define E1000_ICR_MNG 0x00040000 /* Manageability event */ +#define E1000_ICR_DOCK 0x00080000 /* Dock/Undock */ +#define E1000_ICR_INT_ASSERTED 0x80000000 /* If this bit asserted, the driver should claim the interrupt */ +#define E1000_ICR_RXD_FIFO_PAR0 0x00100000 /* queue 0 Rx descriptor FIFO parity error */ +#define E1000_ICR_TXD_FIFO_PAR0 0x00200000 /* queue 0 Tx descriptor FIFO parity error */ +#define E1000_ICR_HOST_ARB_PAR 0x00400000 /* host arb read buffer parity error */ +#define E1000_ICR_PB_PAR 0x00800000 /* packet buffer parity error */ +#define E1000_ICR_RXD_FIFO_PAR1 0x01000000 /* queue 1 Rx descriptor FIFO parity error */ +#define E1000_ICR_TXD_FIFO_PAR1 0x02000000 /* queue 1 Tx descriptor FIFO parity error */ +#define E1000_ICR_ALL_PARITY 0x03F00000 /* all parity error bits */ +#define E1000_ICR_DSW 0x00000020 /* FW changed the status of DISSW bit in the FWSM */ +#define E1000_ICR_PHYINT 0x00001000 /* LAN connected device generates an interrupt */ +#define E1000_ICR_EPRST 0x00100000 /* ME handware reset occurs */ + +/* Interrupt Mask Set */ +#define E1000_IMS_TXDW E1000_ICR_TXDW /* Transmit desc written back */ +#define E1000_IMS_TXQE E1000_ICR_TXQE /* Transmit Queue empty */ +#define E1000_IMS_LSC E1000_ICR_LSC /* Link Status Change */ +#define E1000_IMS_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */ +#define E1000_IMS_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */ +#define E1000_IMS_RXO E1000_ICR_RXO /* rx overrun */ +#define E1000_IMS_RXT0 E1000_ICR_RXT0 /* rx timer intr */ +#define E1000_IMS_MDAC E1000_ICR_MDAC /* MDIO access complete */ +#define E1000_IMS_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */ +#define E1000_IMS_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */ +#define E1000_IMS_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */ +#define E1000_IMS_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */ +#define E1000_IMS_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */ +#define E1000_IMS_TXD_LOW E1000_ICR_TXD_LOW +#define E1000_IMS_SRPD E1000_ICR_SRPD +#define E1000_IMS_ACK E1000_ICR_ACK /* Receive Ack frame */ +#define E1000_IMS_MNG E1000_ICR_MNG /* Manageability event */ +#define E1000_IMS_DOCK E1000_ICR_DOCK /* Dock/Undock */ +#define E1000_IMS_RXD_FIFO_PAR0 E1000_ICR_RXD_FIFO_PAR0 /* queue 0 Rx descriptor FIFO parity error */ +#define E1000_IMS_TXD_FIFO_PAR0 E1000_ICR_TXD_FIFO_PAR0 /* queue 0 Tx descriptor FIFO parity error */ +#define E1000_IMS_HOST_ARB_PAR E1000_ICR_HOST_ARB_PAR /* host arb read buffer parity error */ +#define E1000_IMS_PB_PAR E1000_ICR_PB_PAR /* packet buffer parity error */ +#define E1000_IMS_RXD_FIFO_PAR1 E1000_ICR_RXD_FIFO_PAR1 /* queue 1 Rx descriptor FIFO parity error */ +#define E1000_IMS_TXD_FIFO_PAR1 E1000_ICR_TXD_FIFO_PAR1 /* queue 1 Tx descriptor FIFO parity error */ +#define E1000_IMS_DSW E1000_ICR_DSW +#define E1000_IMS_PHYINT E1000_ICR_PHYINT +#define E1000_IMS_EPRST E1000_ICR_EPRST + +/* Interrupt Mask Clear */ +#define E1000_IMC_TXDW E1000_ICR_TXDW /* Transmit desc written back */ +#define E1000_IMC_TXQE E1000_ICR_TXQE /* Transmit Queue empty */ +#define E1000_IMC_LSC E1000_ICR_LSC /* Link Status Change */ +#define E1000_IMC_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */ +#define E1000_IMC_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */ +#define E1000_IMC_RXO E1000_ICR_RXO /* rx overrun */ +#define E1000_IMC_RXT0 E1000_ICR_RXT0 /* rx timer intr */ +#define E1000_IMC_MDAC E1000_ICR_MDAC /* MDIO access complete */ +#define E1000_IMC_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */ +#define E1000_IMC_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */ +#define E1000_IMC_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */ +#define E1000_IMC_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */ +#define E1000_IMC_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */ +#define E1000_IMC_TXD_LOW E1000_ICR_TXD_LOW +#define E1000_IMC_SRPD E1000_ICR_SRPD +#define E1000_IMC_ACK E1000_ICR_ACK /* Receive Ack frame */ +#define E1000_IMC_MNG E1000_ICR_MNG /* Manageability event */ +#define E1000_IMC_DOCK E1000_ICR_DOCK /* Dock/Undock */ +#define E1000_IMC_RXD_FIFO_PAR0 E1000_ICR_RXD_FIFO_PAR0 /* queue 0 Rx descriptor FIFO parity error */ +#define E1000_IMC_TXD_FIFO_PAR0 E1000_ICR_TXD_FIFO_PAR0 /* queue 0 Tx descriptor FIFO parity error */ +#define E1000_IMC_HOST_ARB_PAR E1000_ICR_HOST_ARB_PAR /* host arb read buffer parity error */ +#define E1000_IMC_PB_PAR E1000_ICR_PB_PAR /* packet buffer parity error */ +#define E1000_IMC_RXD_FIFO_PAR1 E1000_ICR_RXD_FIFO_PAR1 /* queue 1 Rx descriptor FIFO parity error */ +#define E1000_IMC_TXD_FIFO_PAR1 E1000_ICR_TXD_FIFO_PAR1 /* queue 1 Tx descriptor FIFO parity error */ +#define E1000_IMC_DSW E1000_ICR_DSW +#define E1000_IMC_PHYINT E1000_ICR_PHYINT +#define E1000_IMC_EPRST E1000_ICR_EPRST + +// TX and RX descriptor +typedef struct __attribute__((packed)) +{ + uint64_t addr; + uint16_t length; + uint8_t cso; + uint8_t cmd; + uint8_t status; + uint8_t css; + uint16_t special; +} tx_desc_t; + +typedef struct __attribute__((packed)) +{ + uint64_t addr; + uint16_t length; + uint16_t checksum; + uint8_t status; + uint8_t errors; + uint16_t special; +} rx_desc_t; + +/* + * Helper struct to hold private data used to operate your ethernet interface. + */ +typedef struct e1000if { + struct eth_addr *ethaddr; + /* Add whatever per-interface state that is needed here. */ + volatile uint8_t* bar0; + uint8_t* tx_buffers; + uint8_t* rx_buffers; + volatile tx_desc_t* tx_desc; // transmit descriptor buffer + uint16_t tx_tail; + volatile rx_desc_t* rx_desc; // receive descriptor buffer + uint16_t rx_tail; + uint8_t irq; + volatile uint8_t polling; +} e1000if_t; + +/* + * Initialize the network driver for the RealTek RTL8139 family + */ +err_t e1000if_init(struct netif* netif); + +#endif diff --git a/hermit/drivers/net/rtl8139.c b/hermit/drivers/net/rtl8139.c new file mode 100644 index 000000000..f2ca669c0 --- /dev/null +++ b/hermit/drivers/net/rtl8139.c @@ -0,0 +1,484 @@ +/* + * Copyright 2010 Stefan Lankes, Chair for Operating Systems, + * RWTH Aachen University + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This code based mostly on the online manual http://www.lowlevel.eu/wiki/RTL8139 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RX_BUF_LEN 8192 +#define TX_BUF_LEN 4096 +#define MIN(a, b) (a) < (b) ? (a) : (b) + +/* + * 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. + */ +#define INT_MASK (ISR_ROK|ISR_TOK|ISR_RXOVW|ISR_TER|ISR_RER) + +// Beside Receive OK (ROK) interrupt, this mask enable all other interrupts +#define INT_MASK_NO_ROK (ISR_TOK|ISR_RXOVW|ISR_TER|ISR_RER) + +typedef struct { + char *vendor_str; + char *device_str; + uint32_t vendor; + uint32_t device; +} board_t; + +static 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 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) % RX_BUF_LEN; + + 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) % RX_BUF_LEN; +#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) { + i = MIN(q->len, RX_BUF_LEN - rtl8139if->rx_pos); + memcpy((uint8_t*) q->payload, rtl8139if->rx_buffer + rtl8139if->rx_pos, i); + if (i < q->len) // wrap around to end of RxBuffer + memcpy((uint8_t*) q->payload + i, rtl8139if->rx_buffer, q->len - i); + rtl8139if->rx_pos = (rtl8139if->rx_pos + q->len) % RX_BUF_LEN; + } +#if ETH_PAD_SIZE + pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */ +#endif + LINK_STATS_INC(link.recv); + + // forward packet to LwIP + netif->input(p, netif); + } else { + LWIP_DEBUGF(NETIF_DEBUG, ("rtl8139if_rx_inthandler: not enough memory!\n")); + rtl8139if->rx_pos += (rtl8139if->rx_pos + length) % RX_BUF_LEN; + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + } + + // packets are dword aligned + rtl8139if->rx_pos = ((rtl8139if->rx_pos + 4 + 3) & ~0x3) % RX_BUF_LEN; + outportw(rtl8139if->iobase + CAPR, rtl8139if->rx_pos - 0x10); + } else { + LWIP_DEBUGF(NETIF_DEBUG, ("rtl8139if_rx_inthandler: invalid header 0x%x, rx_pos %d\n", (uint32_t) header, rtl8139if->rx_pos)); + LINK_STATS_INC(link.drop); + break; + } + + cmd = inportb(rtl8139if->iobase + CR); + } + + rtl8139if->polling = 0; + // enable all known interrupts + outportw(rtl8139if->iobase + IMR, INT_MASK); +} + +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--; + } + } +} + +/* this function is called in the context of the tcpip thread or the irq handler (by using NO_SYS) */ +static void rtl8139if_poll(void* ctx) +{ + rtl_rx_inthandler(mynetif); +} + +static void rtl8139if_handler(struct state* s) +{ + rtl1839if_t* rtl8139if = mynetif->state; + uint16_t isr_contents; + + // disable all interrupts + outportw(rtl8139if->iobase + IMR, 0x00); + + while (1) { + isr_contents = inportw(rtl8139if->iobase + ISR); + if (isr_contents == 0) + break; + + if ((isr_contents & ISR_ROK) && !rtl8139if->polling) { +#if NO_SYS + rtl8139if_poll(NULL); +#else + if (tcpip_callback_with_block(rtl8139if_poll, NULL, 0) == ERR_OK) { + rtl8139if->polling = 1; + } else { + LWIP_DEBUGF(NETIF_DEBUG, ("rtl8139if_handler: unable to send a poll request to the tcpip thread\n")); + } +#endif + } + + if (isr_contents & ISR_TOK) + rtl_tx_inthandler(mynetif); + + if (isr_contents & ISR_RER) { + LWIP_DEBUGF(NETIF_DEBUG, ("rtl8139if_handler: RX error detected!\n")); + } + + if (isr_contents & ISR_TER) { + LWIP_DEBUGF(NETIF_DEBUG, ("rtl8139if_handler: TX error detected!\n")); + } + + if (isr_contents & ISR_RXOVW) { + LWIP_DEBUGF(NETIF_DEBUG, ("rtl8139if_handler: RX overflow detected!\n")); + } + + outportw(rtl8139if->iobase + ISR, isr_contents & (ISR_RXOVW|ISR_TER|ISR_RER|ISR_TOK|ISR_ROK)); + } + + if (rtl8139if->polling) // now, the tcpip thread will check for incoming messages + outportw(rtl8139if->iobase + IMR, INT_MASK_NO_ROK); + else + outportw(rtl8139if->iobase + IMR, INT_MASK); // enable interrupts +} + +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; + pci_info_t pci_info; + + LWIP_ASSERT("netif != NULL", (netif != NULL)); + + tmp8 = 0; + while (board_tbl[tmp8].vendor_str) { + if (pci_get_device_info(board_tbl[tmp8].vendor, board_tbl[tmp8].device, &pci_info) == 0) + break; + tmp8++; + } + + if (!board_tbl[tmp8].vendor_str) + return ERR_ARG; + + rtl8139if = kmalloc(sizeof(rtl1839if_t)); + if (!rtl8139if) { + LWIP_DEBUGF(NETIF_DEBUG, ("rtl8139if_init: out of memory\n")); + return ERR_MEM; + } + memset(rtl8139if, 0x00, sizeof(rtl1839if_t)); + + rtl8139if->iobase = pci_info.base[0]; + rtl8139if->irq = pci_info.irq; + + /* allocate the receive buffer */ + rtl8139if->rx_buffer = page_alloc(RX_BUF_LEN + 16 /* header size */, VMA_READ|VMA_WRITE); + if (!(rtl8139if->rx_buffer)) { + LWIP_DEBUGF(NETIF_DEBUG, ("rtl8139if_init: out of memory\n")); + kfree(rtl8139if); + return ERR_MEM; + } + memset(rtl8139if->rx_buffer, 0x00, RX_BUF_LEN + 16); + + /* allocate the send buffers */ + rtl8139if->tx_buffer[0] = page_alloc(4*TX_BUF_LEN, VMA_READ|VMA_WRITE); + if (!(rtl8139if->tx_buffer[0])) { + LWIP_DEBUGF(NETIF_DEBUG, ("rtl8139if_init: out of memory\n")); + page_free(rtl8139if->rx_buffer, RX_BUF_LEN + 16); + kfree(rtl8139if); + return ERR_MEM; + } + memset(rtl8139if->tx_buffer[0], 0x00, 4*TX_BUF_LEN); + rtl8139if->tx_buffer[1] = rtl8139if->tx_buffer[0] + TX_BUF_LEN; + rtl8139if->tx_buffer[2] = rtl8139if->tx_buffer[1] + TX_BUF_LEN; + rtl8139if->tx_buffer[3] = rtl8139if->tx_buffer[2] + TX_BUF_LEN; + + netif->state = rtl8139if; + mynetif = netif; + + tmp32 = inportl(rtl8139if->iobase + TCR); + if (tmp32 == 0xFFFFFF) { + LWIP_DEBUGF(NETIF_DEBUG, ("rtl8139if_init: ERROR\n")); + page_free(rtl8139if->rx_buffer, RX_BUF_LEN + 16); + page_free(rtl8139if->tx_buffer[0], 4*TX_BUF_LEN); + kfree(rtl8139if); + memset(netif, 0x00, sizeof(struct netif)); + mynetif = NULL; + + return ERR_ARG; + } + + // determine the hardware revision + //tmp32 = (tmp32 & TCR_HWVERID) >> TCR_HWOFFSET; + + irq_install_handler(rtl8139if->irq+32, rtl8139if_handler); + + /* hardware address length */ + netif->hwaddr_len = ETHARP_HWADDR_LEN; + + 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; tmp8hwaddr[tmp8] = inportb(rtl8139if->iobase + IDR0 + tmp8); + LWIP_DEBUGF(NETIF_DEBUG, ("%02x ", netif->hwaddr[tmp8])); + } + LWIP_DEBUGF(NETIF_DEBUG, ("\n")); + + rtl8139if->ethaddr = (struct eth_addr *) netif->hwaddr; + + // 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"); + page_free(rtl8139if->rx_buffer, RX_BUF_LEN + 16); + page_free(rtl8139if->tx_buffer[0], 4*TX_BUF_LEN); + kfree(rtl8139if); + memset(netif, 0x00, sizeof(struct netif)); + mynetif = NULL; + + 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])); + + // Enable all known interrupts by setting the interrupt mask. + outportw(rtl8139if->iobase + IMR, INT_MASK); + + 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_IGMP; + + return ERR_OK; +} diff --git a/hermit/drivers/net/rtl8139.h b/hermit/drivers/net/rtl8139.h new file mode 100644 index 000000000..ecb43405a --- /dev/null +++ b/hermit/drivers/net/rtl8139.h @@ -0,0 +1,237 @@ +/* + * Copyright 2010 Stefan Lankes, Chair for Operating Systems, + * RWTH Aachen University + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This code based mostly on the online manual http://www.lowlevel.eu/wiki/RTL8139 + */ + +#ifndef __NET_RTL8139_H__ +#define __NET_RTL8139_H__ + +#include +#include + +// the registers are at the following places +#define IDR0 0x0 // the ethernet ID (6bytes) +#define MAR0 0x8 // Multicast (8 bytes) +#define TSD0 0x10 // transmit status of each descriptor (4bytes/descriptor) (C mode) +#define DTCCR 0x10 // Dump Tally Counter Command Register (C+ mode) +#define TSAD0 0x20 // transmit start address of descriptor 0 (4byte, C mode, 4 byte alignment) +#define TSAD1 0x24 // transmit start address of descriptor 1 (4byte, C mode, 4 byte alignment) +#define TNPDS 0x20 // transmit normal priority descriptors start address (8bytes, C+ mode, 256 byte-align) +#define TSAD2 0x28 // transmit start address of descriptor 2 (4byte, C mode, 4 byte alignment) +#define TSAD3 0x2c // transmit start address of descriptor 3 (4byte, C mode, 4 byte alignment) +#define THPDS 0x28 // transmit high priority descriptors start address (8byte, C+ mode, 256 byte-align) +#define RBSTART 0x30 // recieve buffer start address (C mode, 4 byte alignment) +#define ERBCR 0x34 // early recieve byte count (2byte) +#define ERSR 0x36 // early recieve state register (1byte) +#define CR 0x37 // command register (1byte) +#define CAPR 0x38 // current address of packet read (2byte, C mode, initial value 0xFFF0) +#define CBR 0x3a // current buffer address , total recieved byte-count in the Rx buffer (2byte, C mode, initial value 0x0000) +#define IMR 0x3c // interrupt mask register (2byte) +#define ISR 0x3e // interrupt status register (2byte) +#define TCR 0x40 // transmit config register (4byte) +#define RCR 0x44 // receive config register (4byte) +#define TCTR 0x48 // timer count register, write any value and it will reset the count, and count from zero (4byte) +#define MPC 0x4C // missed packet count , number of packets ignored due to RX overflow, 24-bit, write a value to reset (4byte, top is void) +#define CR9346 0x50 // command register for 93C46 (93C56) (1byte) +#define CONFIG0 0x51 // config register 0 (1byte) +#define CONFIG1 0x52 // config register 1 (1byte) +#define TIMINT 0x54 // timer interrupt register , the timeout bit will be set when the value of this == value of TCTR (4byte, when 0 does nothing) +#define MSR 0x58 // media status register (1byte) +#define CONFIG3 0x59 // config register 3 (1byte) +#define CONFIG4 0x5a // config register 4 (1byte) +#define MULINT 0x5c // multiple interrupt select (4byte) +#define RERID 0x5e // revision ID (C+ = 0x10) +#define TSAD 0x60 // transmit status of ALL descriptors (2byte, C mode) +#define BMCR 0x62 // basic mode control register (2byte) +#define BMSR 0x64 // basic mode status register (2byte) +#define ANAR 0x66 // Auto-negotiation advertisement register (2byte) +#define ANLPAR 0x68 // Auto-negotiation link partner register (2byte) +#define ANER 0x6a // Auto-negotiation expansion register (2byte) +#define DIS 0x6c // disconnected counter (2byte) +#define FCSC 0x6e // false carrier sense counter (2byte) +#define NWAYTR 0x70 // N-way test register (2byte) +#define REC 0x72 // RX_ER (counts valid packets) counter (2byte) +#define CSCR 0x74 // CS config register (2byte) +#define PHYS1P 0x78 // PHY parameter 1 (2byte) +#define TWP 0x7c // twister parameter (2byte) +#define PHYS2P 0x80 // PHY parameter 2 +// some power managment registers are here +#define FLASH 0xD4 // flash memory read/write (4byte) +#define CONFIG5 0xD8 // config register 5 (1byte) +#define TPPoll 0xD9 // transmit priority polling (1byte, C+ mode) +#define CPCR 0xE0 // C+ command register (2byte, C+ mode) +#define RDSAR 0xE4 // C+ receive descriptor start address (4byte, C+ mode, 256 byte alignment) +#define ETTR 0xEC // C+ early transmit threshold (1byte, C+ mode) +// some cardbus only stuff goes here +#define MIIR 0xFC // MII register (Auto-detect or MII mode only) + +// Command Register +#define CR_RST 0x10 // Reset, set to 1 to invoke S/W reset, held to 1 while resetting +#define CR_RE 0x08 // Reciever Enable, enables receiving +#define CR_TE 0x04 // Transmitter Enable, enables transmitting +#define CR_BUFE 0x01 // Rx buffer is empty + +// Transmit Configuration Register +#define TCR_HWVERID 0x7CC00000 // mask for hw version ID's +#define TCR_HWOFFSET 22 +#define TCR_IFG 0x3000000 // interframe gap time +#define TCR_LBK1 0x40000 // loopback test +#define TCR_LBK0 0x20000 // loopback test +#define TCR_CRC 0x10000 // append CRC (card adds CRC if 1) +#define TCR_MXDMA2 0x400 // max dma burst +#define TCR_MXDMA1 0x200 // max dma burst +#define TCR_MXDMA0 0x100 // max dma burst +#define TCR_TXRR 0xF0 // Tx retry count, 0 = 16 else retries TXRR * 16 + 16 times +#define TCR_CLRABT 0x01 // Clear abort, attempt retransmit (when in abort state) + +// Media Status Register +#define MSR_TXFCE 0x80 // Tx Flow Control enabled +#define MSR_RXFCE 0x40 // Rx Flow Control enabled +#define MSR_AS 0x10 // Auxilary status +#define MSR_SPEED 0x8 // set if currently talking on 10mbps network, clear if 100mbps +#define MSR_LINKB 0x4 // Link Bad ? +#define MSR_TXPF 0x2 // Transmit Pause flag +#define MSR_RXPF 0x1 // Recieve Pause flag + +// Basic mode control register +#define BMCR_RESET 0x8000 // set the status and control of PHY to default +#define BMCR_SPD100 (1 << 13) // 100 MBit +#define BMCR_SPD1000 (1 << 6) // 1000 MBit +#define BMCR_ANE 0x1000 // enable N-way autonegotiation (ignore above if set) +#define BMCR_RAN 0x400 // restart auto-negotiation +#define BMCR_DUPLEX 0x200 // Duplex mode, generally a value of 1 means full-duplex + +// Receive Configuration Register +#define RCR_ERTH3 0x8000000 // early Rx Threshold 0 +#define RCR_ERTH2 0x4000000 // early Rx Threshold 1 +#define RCR_ERTH1 0x2000000 // early Rx Threshold 2 +#define RCR_ERTH0 0x1000000 // early Rx Threshold 3 +#define RCR_MRINT 0x20000 // Multiple Early interrupt, (enable to make interrupts happen early, yuk) +#define RCR_RER8 0x10000 // Receive Error Packets larger than 8 bytes +#define RCR_RXFTH2 0x8000 // Rx Fifo threshold 0 +#define RCR_RXFTH1 0x4000 // Rx Fifo threshold 1 (set to 110 and it will send to system when 1024bytes have been gathered) +#define RCR_RXFTH0 0x2000 // Rx Fifo threshold 2 (set all these to 1, and it wont FIFO till the full packet is ready) +#define RCR_RBLEN1 0x1000 // Rx Buffer length 0 +#define RCR_RBLEN0 0x800 // Rx Buffer length 1 (C mode, 11 = 64kb, 10 = 32k, 01 = 16k, 00 = 8k) +#define RCR_MXDMA2 0x400 // Max DMA burst size 0 +#define RCR_MXDMA1 0x200 // Max DMA burst size 1 +#define RCR_MXDMA0 0x100 // Max DMA burst size 2 +#define RCR_WRAP 0x80 // (void if buffer size = 64k, C mode, wrap to beginning of Rx buffer if we hit the end) +#define RCR_EEPROMSEL 0x40 // EEPROM type (0 = 9346, 1 = 9356) +#define RCR_AER 0x20 // Accept Error Packets (do we accept bad packets ?) +#define RCR_AR 0x10 // Accept runt packets (accept packets that are too small ?) +#define RCR_AB 0x08 // Accept Broadcast packets (accept broadcasts ?) +#define RCR_AM 0x04 // Accept multicast ? +#define RCR_APM 0x02 // Accept Physical matches (accept packets sent to our mac ?) +#define RCR_AAP 0x01 // Accept packets with a physical address ? + +// Interrupt Status/Mask Register +// Bits in IMR enable/disable interrupts for specific events +// Bits in ISR indicate the status of the card +#define ISR_SERR 0x8000 // System error interrupt +#define ISR_TUN 0x4000 // time out interrupt +#define ISR_SWInt 0x100 // Software interrupt +#define ISR_TDU 0x80 // Tx Descriptor unavailable +#define ISR_FIFOOVW 0x40 // Rx Fifo overflow +#define ISR_PUN 0x20 // Packet underrun/link change +#define ISR_RXOVW 0x10 // Rx overflow/Rx Descriptor unavailable +#define ISR_TER 0x08 // Tx Error +#define ISR_TOK 0x04 // Tx OK +#define ISR_RER 0x02 // Rx Error +#define ISR_ROK 0x01 // Rx OK +#define R39_INTERRUPT_MASK 0x7f + +// CR9346 Command register +#define CR9346_EEM1 0x80 // determine the operating mode +#define CR9346_EEM0 0x40 // 00 = Normal, 01 = Auto-load, 10 = Programming, 11 = Config, Register write enabled +#define CR9346_EECS 0x8 // status of EECS +#define CR9346_EESK 0x4 // status of EESK +#define CR9346_EEDI 0x2 // status of EEDI +#define CR9346_EEDO 0x1 // status of EEDO + +// CONFIG1 stuff +#define CONFIG1_LEDS 0xC0 // leds status +#define CONFIG1_DVRLOAD 0x20 // is the driver loaded ? +#define CONFIG1_LWACT 0x10 // lanwake mode +#define CONFIG1_MEMMAP 0x8 // Memory mapping enabled ? +#define CONFIG1_IOMAP 0x4 // IO map enabled ? +#define CONFIG1_VPD 0x2 // enable the virtal product data +#define CONFIG1_PMEn 0x1 // Power Managment Enable + +// CONFIG3 stuff +#define CONFIG3_GNT 0x80 // Grant Select enable +#define CONFIG3_PARM 0x40 // Parameter auto-load enabled ? +#define CONFIG3_MAGIC 0x20 // Magic packet ? +#define CONFIG3_LINKUP 0x10 // wake computer when link goes up ? +#define CONFIG3_CardB 0x08 // Card Bus stuff enabled ? +#define CONFIG3_CLKRUN 0x04 // enable CLKRUN ? +#define CONFIG3_FRE 0x02 // Function registers enabled ? (cardbus only) +#define CONFIG3_FBBE 0x01 // fast back to back enabled ? + +// CONFIG4 stuff ? +#define CONFIG4_RXFAC 0x80 // Clear Rx Fifo overflow, when enabled the card will clear FIFO overflow automatically +#define CONFIG4_AnaOff 0x40 // Analogue power off ? +#define CONFIG4_LWF 0x20 // Long wake-up frame +#define CONFIG4_LWPME 0x10 // LANWAKE vs PMEB +#define CONFIG4_LWPTN 0x04 // Lan wake pattern ? +#define CONFIG4_PBWAKE 0x01 // pre-boot wakeup + +//Transmit Status of Descriptor0-3 (C mode only) +#define TSD_CRS (1 << 31) // carrier sense lost (during packet transmission) +#define TSD_TABT (1 << 30) // transmission abort +#define TSD_OWC (1 << 29) // out of window collision +#define TSD_CDH (1 << 28) // CD Heart beat (Cleared in 100Mb mode) +#define TSD_NCC 0xF000000 // Number of collisions counted (during transmission) +#define TSD_EARTH 0x3F0000 // threshold to begin transmission (0 = 8bytes, 1->2^6 = * 32bytes) +#define TSD_TOK (1 << 15) // Transmission OK, successful +#define TSD_TUN (1 << 14) // Transmission FIFO underrun +#define TSD_OWN (1 << 13) // Tx DMA operation finished (driver must set to 0 when TBC is written) +#define TSD_SIZE 0x1fff // Descriptor size, the total size in bytes of data to send (max 1792) + +/* + * Helper struct to hold private data used to operate your ethernet interface. + */ +typedef struct rtl1839if { + struct eth_addr *ethaddr; + /* Add whatever per-interface state that is needed here. */ + uint8_t* tx_buffer[4]; + uint8_t* rx_buffer; + uint32_t iobase; + uint32_t tx_queue; + uint32_t tx_complete; + uint16_t rx_pos; + uint8_t tx_inuse[4]; + uint8_t irq; + volatile uint8_t polling; +} rtl1839if_t; + +/* + * Initialize the network driver for the RealTek RTL8139 family + */ +err_t rtl8139if_init(struct netif* netif); + +#endif diff --git a/hermit/kernel/main.c b/hermit/kernel/main.c index 06806b781..ccf1f289c 100644 --- a/hermit/kernel/main.c +++ b/hermit/kernel/main.c @@ -53,11 +53,13 @@ #include #include #include +#include +#include #define HERMIT_PORT 0x494E #define HEMRIT_MAGIC 0x7E317 -static struct netif mmnif_netif; +static struct netif default_netif; static const int sobufsize = 131072; /* @@ -148,11 +150,17 @@ static void tcpip_init_done(void* arg) static int init_netifs(void) { + struct ip_addr ipaddr; + struct ip_addr netmask; + struct ip_addr gw; sys_sem_t sem; + err_t err; if(sys_sem_new(&sem, 0) != ERR_OK) LWIP_ASSERT("Failed to create semaphore", 0); + memset(&default_netif, 0x00, sizeof(struct netif)); + tcpip_init(tcpip_init_done, &sem); sys_sem_wait(&sem); kprintf("TCP/IP initialized.\n"); @@ -160,11 +168,6 @@ static int init_netifs(void) if (!is_single_kernel()) { - struct ip_addr ipaddr; - struct ip_addr netmask; - struct ip_addr gw; - err_t err; - /* Set network address variables */ IP4_ADDR(&gw, 192,168,28,1); IP4_ADDR(&ipaddr, 192,168,28,isle+2); @@ -179,13 +182,13 @@ static int init_netifs(void) * - ip_input : tells him that he should use ip_input */ #if LWIP_TCPIP_CORE_LOCKING_INPUT - if ((err = netifapi_netif_add(&mmnif_netif, &ipaddr, &netmask, &gw, NULL, mmnif_init, tcpip_input)) != ERR_OK) + if ((err = netifapi_netif_add(&default_netif, &ipaddr, &netmask, &gw, NULL, mmnif_init, ip_input)) != ERR_OK) #else /* * Note: Our drivers guarantee that the input function will be called in the context of the tcpip thread. * => Therefore, we are able to use ip_input instead of tcpip_input */ - if ((err = netifapi_netif_add(&mmnif_netif, &ipaddr, &netmask, &gw, NULL, mmnif_init, ip_input)) != ERR_OK) + if ((err = netifapi_netif_add(&default_netif, &ipaddr, &netmask, &gw, NULL, mmnif_init, ip_input)) != ERR_OK) #endif { kprintf("Unable to add the intra network interface: err = %d\n", err); @@ -193,10 +196,47 @@ static int init_netifs(void) } /* tell lwip all initialization is done and we want to set it up */ - netifapi_netif_set_default(&mmnif_netif); - netifapi_netif_set_up(&mmnif_netif); + netifapi_netif_set_default(&default_netif); + netifapi_netif_set_up(&default_netif); + } else { + /* Clear network address because we use DHCP to get an ip address */ + IP4_ADDR(&gw, 0,0,0,0); + IP4_ADDR(&ipaddr, 0,0,0,0); + IP4_ADDR(&netmask, 0,0,0,0); + +#if 0 + /* Note: Our drivers guarantee that the input function will be called in the context of the tcpip thread. + * => Therefore, we are able to use ethernet_input instead of tcpip_input */ + if ((err = netifapi_netif_add(&default_netif, &ipaddr, &netmask, &gw, NULL, rtl8139if_init, ethernet_input)) == ERR_OK) + goto success; + if ((err = netifapi_netif_add(&default_netif, &ipaddr, &netmask, &gw, NULL, e1000if_init, ethernet_input)) == ERR_OK) + goto success; +#endif + + kprintf("Unable to add the network interface: err = %d\n", err); + + return -ENODEV; + +success: + netifapi_netif_set_default(&default_netif); + + kprintf("Starting DHCPD...\n"); + netifapi_dhcp_start(&default_netif); + + int mscnt = 0; + /* wait for ip address */ + while(!default_netif.ip_addr.addr) { + sys_msleep(DHCP_FINE_TIMER_MSECS); + dhcp_fine_tmr(); + mscnt += DHCP_FINE_TIMER_MSECS; + if (mscnt >= DHCP_COARSE_TIMER_SECS*1000) { + dhcp_coarse_tmr(); + mscnt = 0; + } + } } + return 0; } diff --git a/hermit/lwip b/hermit/lwip index 4042efff1..cae72f321 160000 --- a/hermit/lwip +++ b/hermit/lwip @@ -1 +1 @@ -Subproject commit 4042efff160d0006d002109c1b9e423c782bd9e3 +Subproject commit cae72f3218c5c523b4ede3b38a16163e6520ee49