/* * Copyright (c) 2007, 2008 The tyndur Project. All rights reserved. * * This code is derived from software contributed to the tyndur Project * by Kevin Wolf. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * * 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 COPYRIGHT HOLDERS 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 <stdint.h> #include <stdio.h> #include <stddef.h> #include <string.h> #include <stdlib.h> #include "cdi.h" #include "cdi/misc.h" #include "cdi/pci.h" #include "device.h" #include "e1000_io.h" #undef DEBUG #define PHYS(netcard, field) \ ((uintptr_t) netcard->phys + offsetof(struct e1000_device, field)) static void e1000_handle_interrupt(struct cdi_device* device); static uint64_t get_mac_address(struct e1000_device* device); static uint16_t e1000_eeprom_read(struct e1000_device* device, uint16_t offset) { uint32_t eerd; reg_outl(device, REG_EEPROM_READ, (offset << 8) | EERD_START); while (((eerd = reg_inl(device, REG_EEPROM_READ)) & EERD_DONE) == 0); printf("eerd = %8x\n", eerd); return (eerd >> 16); } static void reset_nic(struct e1000_device* netcard) { uint64_t mac; int i; // Rx/Tx deaktivieren reg_outl(netcard, REG_RX_CTL, 0); reg_outl(netcard, REG_TX_CTL, 0); // Reset ausfuehren reg_outl(netcard, REG_CTL, CTL_PHY_RESET); cdi_sleep_ms(10); reg_outl(netcard, REG_CTL, CTL_RESET); cdi_sleep_ms(10); while (reg_inl(netcard, REG_CTL) & CTL_RESET); // Kontrollregister initialisieren reg_outl(netcard, REG_CTL, CTL_AUTO_SPEED | CTL_LINK_UP); // Rx/Tx-Ring initialisieren reg_outl(netcard, REG_RXDESC_ADDR_HI, 0); reg_outl(netcard, REG_RXDESC_ADDR_LO, PHYS(netcard, rx_desc[0])); reg_outl(netcard, REG_RXDESC_LEN, RX_BUFFER_NUM * sizeof(struct e1000_rx_descriptor)); reg_outl(netcard, REG_RXDESC_HEAD, 0); reg_outl(netcard, REG_RXDESC_TAIL, RX_BUFFER_NUM - 1); reg_outl(netcard, REG_RX_DELAY_TIMER, 0); reg_outl(netcard, REG_RADV, 0); reg_outl(netcard, REG_TXDESC_ADDR_HI, 0); reg_outl(netcard, REG_TXDESC_ADDR_LO, PHYS(netcard, tx_desc[0])); reg_outl(netcard, REG_TXDESC_LEN, TX_BUFFER_NUM * sizeof(struct e1000_tx_descriptor)); reg_outl(netcard, REG_TXDESC_HEAD, 0); reg_outl(netcard, REG_TXDESC_TAIL, 0); reg_outl(netcard, REG_TX_DELAY_TIMER, 0); reg_outl(netcard, REG_TADV, 0); // VLANs deaktivieren reg_outl(netcard, REG_VET, 0); // MAC-Filter mac = get_mac_address(netcard); reg_outl(netcard, REG_RECV_ADDR_LIST, mac & 0xFFFFFFFF); reg_outl(netcard, REG_RECV_ADDR_LIST + 4, ((mac >> 32) & 0xFFFF) | RAH_VALID); // Rx-Deskriptoren aufsetzen for (i = 0; i < RX_BUFFER_NUM; i++) { netcard->rx_desc[i].length = RX_BUFFER_SIZE; netcard->rx_desc[i].buffer = PHYS(netcard, rx_buffer[i * RX_BUFFER_SIZE]); #ifdef DEBUG printf("e1000: [%d] Rx: Buffer @ phys %08x, Desc @ phys %08x\n", i, netcard->rx_desc[i].buffer, PHYS(netcard, rx_desc[i])); #endif } netcard->tx_cur_buffer = 0; netcard->rx_cur_buffer = 0; // Interrupts aktivieren reg_outl(netcard, REG_INTR_MASK_CLR, 0xFFFF); reg_outl(netcard, REG_INTR_MASK, 0xFFFF); // Rx/Tx aktivieren reg_outl(netcard, REG_RX_CTL, RCTL_ENABLE | RCTL_BROADCAST); reg_outl(netcard, REG_TX_CTL, TCTL_ENABLE | TCTL_PADDING | TCTL_COLL_TSH | TCTL_COLL_DIST); } static uint64_t get_mac_address(struct e1000_device* device) { uint64_t mac = 0; int i; for (i = 0; i < 3; i++) { mac |= (uint64_t) e1000_eeprom_read( device, EEPROM_OFS_MAC + i) << (i * 16); } return mac; } struct cdi_device* e1000_init_device(struct cdi_bus_data* bus_data) { struct cdi_pci_device* pci = (struct cdi_pci_device*) bus_data; struct e1000_device* netcard; void* phys_device; if (!((pci->vendor_id == 0x8086) && (pci->device_id == 0x100e))) { return NULL; } cdi_alloc_phys_mem(sizeof(*netcard), (void**) &netcard, &phys_device); memset(netcard, 0, sizeof(*netcard)); netcard->net.send_packet = e1000_send_packet; netcard->phys = phys_device; netcard->net.dev.bus_data = (struct cdi_bus_data*) pci; // PCI-bezogenes Zeug initialisieren netcard->revision = pci->rev_id; cdi_register_irq(pci->irq, e1000_handle_interrupt, &netcard->net.dev); cdi_pci_alloc_ioports(pci); cdi_list_t reslist = pci->resources; struct cdi_pci_resource* res; int i; for (i = 0; (res = cdi_list_get(reslist, i)); i++) { if (res->type == CDI_PCI_MEMORY) { netcard->mem_base = cdi_alloc_phys_addr(res->length, res->start); } } // Karte initialisieren printf("e1000: IRQ %d, MMIO an %p Revision:%d\n", pci->irq, netcard->mem_base, netcard->revision); printf("e1000: Fuehre Reset der Karte durch\n"); reset_nic(netcard); netcard->net.mac = get_mac_address(netcard); printf("e1000: MAC-Adresse: %012llx\n", (uint64_t) netcard->net.mac); cdi_net_device_init(&netcard->net); return &netcard->net.dev; } void e1000_remove_device(struct cdi_device* device) { } /** * Die Uebertragung von Daten geschieht durch einen Ring von * Transmit-Deskriptoren, die jeweils ein zu uebertragendes Paket * beschreiben. * * Die Hardware kennt dabei zwei besondere Deskriptoren, die Head und * Tail heissen. Wenn der Treiber ein neues Paket zum Senden einstellt, * fuegt er einen neuen Deskriptor nach Tail ein und erhoeht Tail. * * Die Hardware erhoeht ihrerseits Head, wenn sie ein Paket abgeschickt hat. * Wenn Head = Tail ist, ist die Sendewarteschlange leer. */ void e1000_send_packet(struct cdi_net_device* device, void* data, size_t size) { struct e1000_device* netcard = (struct e1000_device*) device; uint32_t cur, head; #ifdef DEBUG printf("e1000: e1000_send_packet\n"); #endif // Aktuellen Deskriptor erhoehen cur = netcard->tx_cur_buffer; netcard->tx_cur_buffer++; netcard->tx_cur_buffer %= TX_BUFFER_NUM; // Head auslesen head = reg_inl(netcard, REG_TXDESC_HEAD); if (netcard->tx_cur_buffer == head) { printf("e1000: Kein Platz in der Sendewarteschlange!\n"); return; } // Buffer befuellen if (size > TX_BUFFER_SIZE) { size = TX_BUFFER_SIZE; } memcpy(netcard->tx_buffer + cur * TX_BUFFER_SIZE, data, size); // TX-Deskriptor setzen und Tail erhoehen netcard->tx_desc[cur].cmd = TX_CMD_EOP | TX_CMD_IFCS; netcard->tx_desc[cur].length = size; netcard->tx_desc[cur].buffer = PHYS(netcard, tx_buffer) + (cur * TX_BUFFER_SIZE); #ifdef DEBUG printf("e1000: Setze Tail auf %d, Head = %d\n", netcard->tx_cur_buffer, head); #endif reg_outl(netcard, REG_TXDESC_TAIL, netcard->tx_cur_buffer); } static void e1000_handle_interrupt(struct cdi_device* device) { struct e1000_device* netcard = (struct e1000_device*) device; uint32_t icr = reg_inl(netcard, REG_INTR_CAUSE); #ifdef DEBUG printf("e1000: Interrupt, ICR = %08x\n", icr); #endif if (icr & ICR_RECEIVE) { uint32_t head = reg_inl(netcard, REG_RXDESC_HEAD); while (netcard->rx_cur_buffer != head) { size_t size = netcard->rx_desc[netcard->rx_cur_buffer].length; uint8_t status = netcard->rx_desc[netcard->rx_cur_buffer].status; // Wenn Descriptor Done nicht gesetzt ist, war die Hardware // noch nicht gant fertig mit Kopieren if ((status & 0x1) == 0) { break; } // 4 Bytes CRC von der Laenge abziehen size -= 4; #ifdef DEBUG printf("e1000: %d Bytes empfangen (status = %x)\n", size, status); /* int i; for (i = 0; i < (size < 49 ? size : 49); i++) { printf("%02hhx ", netcard->rx_buffer[ netcard->rx_cur_buffer * RX_BUFFER_SIZE + i]); if (i % 25 == 0) { printf("\n"); } } printf("\n\n"); */ #endif cdi_net_receive( (struct cdi_net_device*) netcard, &netcard->rx_buffer[netcard->rx_cur_buffer * RX_BUFFER_SIZE], size); netcard->rx_cur_buffer++; netcard->rx_cur_buffer %= RX_BUFFER_NUM; } if (netcard->rx_cur_buffer == head) { reg_outl(netcard, REG_RXDESC_TAIL, (head + RX_BUFFER_NUM - 1) % RX_BUFFER_NUM); } else { reg_outl(netcard, REG_RXDESC_TAIL, netcard->rx_cur_buffer); } } else if (icr & ICR_TRANSMIT) { // Nichts zu tun } else { #ifdef DEBUG printf("e1000: Unerwarteter Interrupt.\n"); #endif } }