From c3766893748be06a6c8f806cf68bb5b264af865e Mon Sep 17 00:00:00 2001 From: beveloper Date: Mon, 3 May 2004 21:50:29 +0000 Subject: [PATCH] Removed REG*() macros and replaced them with read*() write*() macros. Added chipset configuration, phy access, tx and rx interrupt handling, read and write hooks. Nearly working git-svn-id: file:///srv/svn/repos/haiku/trunk/current@7382 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../kernel/drivers/network/rtl8169/device.c | 546 +++++++++++++++--- .../kernel/drivers/network/rtl8169/device.h | 45 +- 2 files changed, 480 insertions(+), 111 deletions(-) diff --git a/src/add-ons/kernel/drivers/network/rtl8169/device.c b/src/add-ons/kernel/drivers/network/rtl8169/device.c index be199854a9..79361a9fa5 100644 --- a/src/add-ons/kernel/drivers/network/rtl8169/device.c +++ b/src/add-ons/kernel/drivers/network/rtl8169/device.c @@ -1,3 +1,6 @@ +/* Realtek RTL8169 Family Driver + * Copyright (C) 2004 Marcus Overhagen . All rights reserved. + */ #include #include #include @@ -5,6 +8,8 @@ #include #include +//#define DEBUG + #include "debug.h" #include "device.h" #include "driver.h" @@ -13,34 +18,120 @@ static int32 gOpenMask = 0; +void +write_phy_reg(rtl8169_device *device, int reg, uint16 value) +{ + int i; + write32(REG_PHYAR, 0x80000000 | (reg & 0x1f) << 16 | value); + snooze(1000); + for (i = 0; i < 2000; i++) { + if ((read32(REG_PHYAR) & 0x80000000) == 0) + break; + snooze(100); + } +} + +uint16 +read_phy_reg(rtl8169_device *device, int reg) +{ + uint32 v; + int i; + write32(REG_PHYAR, (reg & 0x1f) << 16); + snooze(1000); + for (i = 0; i < 2000; i++) { + v = read32(REG_PHYAR); + if (v & 0x80000000) + return v & 0xffff; + snooze(100); + } + return 0xffff; +} + +void +write_phy_reg_bit(rtl8169_device *device, int reg, int bitnum, int bitval) +{ + uint16 val = read_phy_reg(device, reg); + if (bitval == 1) + val |= (1 << bitnum); + else + val &= ~(1 << bitnum); + write_phy_reg(device, reg, val); +} + +void +phy_config(rtl8169_device *device) +{ + TRACE("phy_config()\n"); + + if (device->phy_version == 0 || device->phy_version == 1) { + uint16 val; + TRACE("performing phy init\n"); + } + + write_phy_reg(device, 0x04, 0x01e1); // 10/100 capability + write_phy_reg(device, 0x09, 0x0200); // 1000 capability + write_phy_reg(device, 0x00, 0x1200); // enable and rstart NWAY +} + + +void +dump_phy_stat(rtl8169_device *device) +{ + uint32 v = read8(REG_PHY_STAT); + if (v & PHY_STAT_EnTBI) { + uint32 tbi = read32(REG_TBICSR); + TRACE("TBI mode active\n"); + if (tbi & TBICSR_ResetTBI) + TRACE("TBICSR_ResetTBI\n"); + if (tbi & TBICSR_TBILoopBack) + TRACE("TBICSR_TBILoopBack\n"); + if (tbi & TBICSR_TBINWEn) + TRACE("TBICSR_TBINWEn\n"); + if (tbi & TBICSR_TBIReNW) + TRACE("TBICSR_TBIReNW\n"); + if (tbi & TBICSR_TBILinkOk) + TRACE("TBICSR_TBILinkOk\n"); + if (tbi & TBICSR_NWComplete) + TRACE("TBICSR_NWComplete\n"); + } else { + TRACE("TBI mode NOT active\n"); + if (v & PHY_STAT_1000MF) + TRACE("PHY_STAT_1000MF\n"); + if (v & PHY_STAT_100M) + TRACE("PHY_STAT_100M\n"); + if (v & PHY_STAT_10M) + TRACE("PHY_STAT_10M\n"); + } + if (v & PHY_STAT_TxFlow) + TRACE("PHY_STAT_TxFlow\n"); + if (v & PHY_STAT_RxFlow) + TRACE("PHY_STAT_RxFlow\n"); + if (v & PHY_STAT_LinkSts) + TRACE("PHY_STAT_LinkSts\n"); + if (v & PHY_STAT_FullDup) + TRACE("PHY_STAT_FullDup\n"); +} + + status_t init_buf_desc(rtl8169_device *device) { void *rx_buf_desc_virt, *rx_buf_desc_phy; void *tx_buf_desc_virt, *tx_buf_desc_phy; - void *tx_prio_buf_desc_virt, *tx_prio_buf_desc_phy; void *tx_buf_virt, *tx_buf_phy; void *rx_buf_virt, *rx_buf_phy; int i; device->txBufArea = alloc_mem(&tx_buf_virt, &tx_buf_phy, TX_DESC_COUNT * FRAME_SIZE, 0, "rtl8169 tx buf"); device->rxBufArea = alloc_mem(&rx_buf_virt, &rx_buf_phy, RX_DESC_COUNT * FRAME_SIZE, 0, "rtl8169 rx buf"); - device->txDescArea = alloc_mem(&tx_buf_desc_virt, &tx_buf_desc_phy, TX_DESC_COUNT * sizeof(tx_desc), 0, "rtl8169 tx desc"); - device->txPrioDescArea = alloc_mem(&tx_prio_buf_desc_virt, &tx_prio_buf_desc_phy, sizeof(tx_desc), 0, "rtl8169 prio tx desc"); - device->rxDescArea = alloc_mem(&rx_buf_desc_virt, &rx_buf_desc_phy, RX_DESC_COUNT * sizeof(rx_desc), 0, "rtl8169 rx desc"); - if (device->txBufArea < B_OK || device->rxBufArea < B_OK || device->txDescArea < B_OK || device->txPrioDescArea < B_OK || device->rxDescArea < B_OK) + device->txDescArea = alloc_mem(&tx_buf_desc_virt, &tx_buf_desc_phy, TX_DESC_COUNT * sizeof(buf_desc), 0, "rtl8169 tx desc"); + device->rxDescArea = alloc_mem(&rx_buf_desc_virt, &rx_buf_desc_phy, RX_DESC_COUNT * sizeof(buf_desc), 0, "rtl8169 rx desc"); + if (device->txBufArea < B_OK || device->rxBufArea < B_OK || device->txDescArea < B_OK || device->rxDescArea < B_OK) return B_NO_MEMORY; - device->txDesc = (tx_desc *) tx_buf_desc_virt; - device->txPrioDesc = (tx_desc *) tx_prio_buf_desc_virt; - device->rxDesc = (rx_desc *) rx_buf_desc_virt; + device->txDesc = (buf_desc *) tx_buf_desc_virt; + device->rxDesc = (buf_desc *) rx_buf_desc_virt; - // setup unused priority transmit descriptor - device->txPrioDesc->stat_len = TX_DESC_FS | TX_DESC_LS | TX_DESC_EOR; - device->txPrioDesc->vlan = 0; - device->txPrioDesc->buf_low = 0; - device->txPrioDesc->buf_high = 0; - // setup transmit descriptors for (i = 0; i < TX_DESC_COUNT; i++) { device->txBuf[i] = (char *)tx_buf_virt + (i * FRAME_SIZE); @@ -53,25 +144,111 @@ init_buf_desc(rtl8169_device *device) // setup receive descriptors for (i = 0; i < RX_DESC_COUNT; i++) { device->rxBuf[i] = (char *)rx_buf_virt + (i * FRAME_SIZE); - device->rxDesc[i].stat_len = RX_DESC_FS | RX_DESC_LS | RX_DESC_OWN; + device->rxDesc[i].stat_len = RX_DESC_OWN | FRAME_SIZE; device->rxDesc[i].buf_low = (uint32)((char *)rx_buf_phy + (i * FRAME_SIZE)); device->rxDesc[i].buf_high = 0; } device->rxDesc[i - 1].stat_len |= RX_DESC_EOR; - LOG("tx_buf_desc_phy = %p\n", tx_buf_desc_phy); - *REG32(REG_TNPDS_LOW) = (uint32)tx_buf_desc_phy; - LOG("REG_TNPDS_LOW = %p\n", (void *) *REG32(REG_TNPDS_LOW)); - - *REG32(REG_TNPDS_HIGH) = 0; - *REG32(REG_THPDS_LOW) = (uint32)tx_prio_buf_desc_phy; - *REG32(REG_THPDS_HIGH) = 0; - *REG32(REG_RDSAR_LOW) = (uint32)rx_buf_desc_phy; - *REG32(REG_RDSAR_HIGH) = 0; + write32(REG_RDSAR_LOW, (uint32)rx_buf_desc_phy); + write32(REG_RDSAR_HIGH, 0); + write32(REG_TNPDS_LOW, (uint32)tx_buf_desc_phy); + write32(REG_TNPDS_HIGH, 0); + write32(REG_THPDS_LOW, 0); // high priority tx is unused + write32(REG_THPDS_HIGH, 0); return B_OK; } +inline void +rtl8169_tx_int(rtl8169_device *device) +{ + int32 limit; + int32 count; + + acquire_spinlock(&device->txSpinlock); + + for (count = 0, limit = device->txUsed; limit > 0; limit--) { + if (device->txDesc[device->txIntIndex].stat_len & TX_DESC_OWN) + break; + device->txIntIndex = (device->txIntIndex + 1) % TX_DESC_COUNT; + count++; + } + +// dprintf("tx int, txUsed %d, count %d\n", device->txUsed, count); + + device->txUsed -= count; + + release_spinlock(&device->txSpinlock); + + if (count) + release_sem_etc(device->txFreeSem, count, B_DO_NOT_RESCHEDULE); +} + + +inline void +rtl8169_rx_int(rtl8169_device *device) +{ + int32 limit; + int32 count; + + acquire_spinlock(&device->rxSpinlock); + + for (count = 0, limit = device->rxFree; limit > 0; limit--) { + if (device->rxDesc[device->rxIntIndex].stat_len & RX_DESC_OWN) + break; + device->rxIntIndex = (device->rxIntIndex + 1) % RX_DESC_COUNT; + count++; + } + +// dprintf("rx int, rxFree %d, count %d\n", device->rxFree, count); + + device->rxFree -= count; + + release_spinlock(&device->rxSpinlock); + + if (count) + release_sem_etc(device->rxReadySem, count, B_DO_NOT_RESCHEDULE); +} + + +int32 +rtl8169_int(void *data) +{ + rtl8169_device *device = (rtl8169_device *)data; + uint16 stat; + int32 ret; + + stat = read16(REG_INT_STAT); + if (stat == 0 || stat == 0xffff) + return B_UNHANDLED_INTERRUPT; + + write16(REG_INT_STAT, stat); + ret = B_HANDLED_INTERRUPT; + + if (stat & INT_FOVW) { + TRACE("INT_FOVW\n"); + } + + if (stat & INT_PUN) { + TRACE("INT_PUN\n"); + dump_phy_stat(device); + } + + if (stat & (INT_TOK | INT_TER)) { + rtl8169_tx_int(device); + ret = B_INVOKE_SCHEDULER; + } + + if (stat & (INT_ROK | INT_RER)) { + rtl8169_rx_int(device); + ret = B_INVOKE_SCHEDULER; + } + + return ret; +} + + status_t rtl8169_open(const char *name, uint32 flags, void** cookie) { @@ -82,14 +259,14 @@ rtl8169_open(const char *name, uint32 flags, void** cookie) int mask; int i; - LOG("rtl8169_open()\n"); + TRACE("rtl8169_open()\n"); for (dev_id = 0; (deviceName = gDevNameList[dev_id]) != NULL; dev_id++) { if (!strcmp(name, deviceName)) break; } if (deviceName == NULL) { - LOG("invalid device name"); + ERROR("invalid device name"); return B_ERROR; } @@ -108,74 +285,160 @@ rtl8169_open(const char *name, uint32 flags, void** cookie) device->devId = dev_id; device->pciInfo = gDevList[dev_id]; - device->blockFlag = (flags & O_NONBLOCK) ? B_TIMEOUT : 0; + device->nonblocking = (flags & O_NONBLOCK) ? true : false; device->closed = false; + + device->rxSpinlock = 0; device->rxReadySem = create_sem(0, "rtl8169 rx ready"); + device->rxNextIndex = 0; + device->rxIntIndex = 0; + device->rxFree = RX_DESC_COUNT; + + device->txSpinlock = 0; device->txFreeSem = create_sem(TX_DESC_COUNT, "rtl8169 tx free"); - device->txIndex = 0; - device->rxIndex = 0; + device->txNextIndex = 0; + device->txIntIndex = 0; + device->txUsed = 0; // enable busmaster and memory mapped access, disable io port access val = gPci->read_pci_config(device->pciInfo->bus, device->pciInfo->device, device->pciInfo->function, PCI_command, 2); val = PCI_PCICMD_BME | PCI_PCICMD_MSE | (val & ~PCI_PCICMD_IOS); gPci->write_pci_config(device->pciInfo->bus, device->pciInfo->device, device->pciInfo->function, PCI_command, 2, val); + // adjust PCI latency timer + TRACE("changing PCI latency to 0x40\n"); + gPci->write_pci_config(device->pciInfo->bus, device->pciInfo->device, device->pciInfo->function, PCI_latency, 1, 0x40); + + // get IRQ + device->irq = gPci->read_pci_config(device->pciInfo->bus, device->pciInfo->device, device->pciInfo->function, PCI_interrupt_line, 1); + if (device->irq == 0 || device->irq == 0xff) { + ERROR("no IRQ assigned\n"); + goto err; + } + + TRACE("IRQ %d\n", device->irq); + // map registers into memory val = gPci->read_pci_config(device->pciInfo->bus, device->pciInfo->device, device->pciInfo->function, 0x14, 4); val &= PCI_address_memory_32_mask; - LOG("hardware register address %p\n", (void *) val); + TRACE("hardware register address %p\n", (void *) val); device->refArea = map_mem(&device->regAddr, (void *)val, 256, 0, "rtl8169 register"); - if (device->refArea < B_OK) + if (device->refArea < B_OK) { + ERROR("can't map hardware registers\n"); goto err; - - LOG("mapped %p to %p\n", (void *)val, device->regAddr); - - // get IRQ - device->irq = gPci->read_pci_config(device->pciInfo->bus, device->pciInfo->device, device->pciInfo->function, PCI_interrupt_line, 1); - if (device->irq == 0 || device->irq == 0xff) - goto err; - - LOG("IRQ %d\n", device->irq); - - // disable receiver & transmitter - *REG8(REG_CR) &= ~REG_CR_RE | REG_CR_TE; - - // setup buffer descriptors and buffers - if (init_buf_desc(device) != B_OK) - goto err; - - // soft reset - *REG8(REG_CR) |= REG_CR_RST; - for (i = 0; ((*REG8(REG_CR)) & REG_CR_RST) && i < 100; i++) - snooze(10000); - if (i == 100) { - LOG("reset failed\n"); - goto err; } - LOG("reset done, i %d\n", i); - - for (i = 0; i < 6; i++) - device->macaddr[i] = *REG8(i); - dprintf("MAC %02x:%02x:%02x:%02x:%02x:%02x\n", + TRACE("mapped registers to %p\n", device->regAddr); + + // disable receiver & transmitter + write8(REG_CR, read8(REG_CR) & ~(CR_RE | CR_TE)); + + // do a soft reset, does also initialize chip with buffer descriptors + write8(REG_CR, read8(REG_CR) | CR_RST); + for (i = 0; (read8(REG_CR) & CR_RST) && i < 1000; i++) + snooze(10); + if (i == 1000) { + ERROR("hardware reset failed\n"); + goto err; + } + + TRACE("reset done\n"); + + // get MAC hardware version + device->mac_version = ((read32(REG_TX_CONFIG) & 0x7c000000) >> 25) | ((read32(REG_TX_CONFIG) & 0x00800000) >> 23); + TRACE("8169 Mac Version %d\n", device->mac_version); + if (device->mac_version > 0) { // this is a RTL8169s single chip + // get PHY hardware version + device->phy_version = read_phy_reg(device, 0x03) & 0x0f; + TRACE("8169 Phy Version %d\n", device->phy_version); + } else { + device->phy_version = 0; + TRACE("8169 Phy Version unknown\n"); + } + + if (device->mac_version == 1) { + TRACE("Setting MAC Reg C+CR 0x82h = 0x01h\n"); + write8(0x82, 0x01); + TRACE("Setting PHY Reg 0x0bh = 0x00h\n"); + write_phy_reg(device, 0x0b, 0x0000); + } + + // configure PHY + phy_config(device); + + // initialize MAC address + for (i = 0; i < 6; i++) + device->macaddr[i] = read8(i); + + TRACE("MAC %02x:%02x:%02x:%02x:%02x:%02x\n", device->macaddr[0], device->macaddr[1], device->macaddr[2], device->macaddr[3], device->macaddr[4], device->macaddr[5]); + + // setup interrupt handler + if (install_io_interrupt_handler(device->irq, rtl8169_int, device, 0) < B_OK) { + ERROR("can't install interrupt handler\n"); + goto err; + } + + write16(0xe0, read16(0xe0)); // write CR+ command + + write16(0xe0, read16(0xe0) | 0x0003); // don't know what this does, BSD says "enable C+ Tx/Rx" + + if (device->mac_version == 1) { + TRACE("Setting Reg C+CR bit 3 and bit 14 to 1\n"); + write16(0xe0, read16(0xe0) | 0x4008); + } + + // setup buffer descriptors and buffers + if (init_buf_desc(device) != B_OK) { + ERROR("setting up buffer descriptors failed\n"); + goto err; + } - LOG("tx base = %p\n", (void *) *REG32(REG_TNPDS_LOW)); - // enable receiver & transmitter -// *REG8(REG_CR) |= REG_CR_RE | REG_CR_TE; + write8(REG_CR, read8(REG_CR) | CR_RE | CR_TE); + + write8(REG_9346CR, 0xc0); // enable config access + write8(REG_CONFIG1, read8(REG_CONFIG1) & ~1); // disable power management + write8(REG_9346CR, 0x00); // disable config access + + write8(0xec, 0x3f); // disable early transmit treshold + write16(0xda, FRAME_SIZE); // receive packet maximum size + write16(0x5c, read16(0x5c) & 0xf000); // disable early receive interrupts + + write32(0x4c, 0); // RxMissed ??? + + // setup receive config, can only be done when receiver is enabled! + // 1024 byte FIFO treshold, 1024 DMA burst + write32(REG_RX_CONFIG, (read32(REG_RX_CONFIG) & RX_CONFIG_MASK) + | (0x6 << RC_CONFIG_RXFTH_Shift) | (0x6 << RC_CONFIG_MAXDMA_Shift) + | RX_CONFIG_AcceptBroad | RX_CONFIG_AcceptMulti | RX_CONFIG_AcceptMyPhys); + + write32(0x8, 0); // multicast filter + write32(0xc, 0); // multicast filter + + // setup transmit config, can only be done when transmitter is enabled! + // append CRC, 1024 DMA burst + write32(REG_TX_CONFIG, (read32(REG_TX_CONFIG) & ~(0x10000 | (1 << 8))) | (0x6 << 8)); + + // clear pending interrupt status + write16(REG_INT_STAT, 0xffff); + + // enable used interrupts + write16(REG_INT_MASK, INT_FOVW | INT_PUN | INT_TER | INT_TOK | INT_RER | INT_ROK); + + dump_phy_stat(device); + return B_OK; + err: - LOG("error!\n"); delete_sem(device->rxReadySem); delete_sem(device->txFreeSem); delete_area(device->refArea); delete_area(device->txBufArea); delete_area(device->rxBufArea); delete_area(device->txDescArea); - delete_area(device->txPrioDescArea); delete_area(device->rxDescArea); free(device); atomic_and(&gOpenMask, ~(1 << dev_id)); @@ -187,7 +450,7 @@ status_t rtl8169_close(void* cookie) { rtl8169_device *device = (rtl8169_device *)cookie; - LOG("rtl8169_close()\n"); + TRACE("rtl8169_close()\n"); device->closed = true; release_sem(device->rxReadySem); @@ -201,10 +464,16 @@ status_t rtl8169_free(void* cookie) { rtl8169_device *device = (rtl8169_device *)cookie; - LOG("rtl8169_free()\n"); + TRACE("rtl8169_free()\n"); // disable receiver & transmitter - *REG8(REG_CR) &= ~REG_CR_RE | REG_CR_TE; + write8(REG_CR, read8(REG_CR) & ~(CR_RE | CR_TE)); + + // disable interrupts + write16(REG_INT_MASK, 0); + + // well... + remove_io_interrupt_handler (device->irq, rtl8169_int, device); delete_sem(device->rxReadySem); delete_sem(device->txFreeSem); @@ -212,7 +481,6 @@ rtl8169_free(void* cookie) delete_area(device->txBufArea); delete_area(device->rxBufArea); delete_area(device->txDescArea); - delete_area(device->txPrioDescArea); delete_area(device->rxDescArea); free(device); atomic_and(&gOpenMask, ~(1 << device->devId)); @@ -224,24 +492,58 @@ status_t rtl8169_read(void* cookie, off_t position, void *buf, size_t* num_bytes) { rtl8169_device *device = (rtl8169_device *)cookie; + cpu_status cpu; status_t stat; - LOG("rtl8169_read()\n"); + int len; + TRACE("rtl8169_read() enter\n"); - if (device->closed) + if (device->closed) { + TRACE("rtl8169_read() interrupted 1\n"); return B_INTERRUPTED; + } retry: - stat = acquire_sem_etc(device->rxReadySem, 1, B_CAN_INTERRUPT | device->blockFlag, 0); - if (device->closed) + stat = acquire_sem_etc(device->rxReadySem, 1, B_CAN_INTERRUPT | (device->nonblocking ? B_TIMEOUT : 0), 0); + if (device->closed) { + // TRACE("rtl8169_read() interrupted 2\n"); // net_server will crash if we print this (race condition in net_server?) return B_INTERRUPTED; + } if (stat == B_WOULD_BLOCK) { + TRACE("rtl8169_read() would block (OK 0 bytes)\n"); *num_bytes = 0; return B_OK; } - if (stat != B_OK) + if (stat != B_OK) { + TRACE("rtl8169_read() error\n"); return B_ERROR; + } + if (device->rxDesc[device->rxNextIndex].stat_len & RX_DESC_OWN) { + ERROR("rtl8169_read() buffer still in use\n"); + goto retry; + } + len = (device->rxDesc[device->rxNextIndex].stat_len & RX_DESC_LEN_MASK); + len -= 4; // remove CRC that Realtek always appends + if (len < 0) + len = 0; + if (len > *num_bytes) + len = *num_bytes; + + memcpy(buf, device->rxBuf[device->rxNextIndex], len); + *num_bytes = len; + + cpu = disable_interrupts(); + acquire_spinlock(&device->rxSpinlock); + + device->rxDesc[device->rxNextIndex].stat_len = RX_DESC_OWN | FRAME_SIZE | (device->rxDesc[device->rxNextIndex].stat_len & RX_DESC_EOR); + device->rxFree++; + + release_spinlock(&device->rxSpinlock); + restore_interrupts(cpu); + device->rxNextIndex = (device->rxNextIndex + 1) % RX_DESC_COUNT; + + TRACE("rtl8169_read() leave\n"); return B_OK; } @@ -250,72 +552,126 @@ status_t rtl8169_write(void* cookie, off_t position, const void* buffer, size_t* num_bytes) { rtl8169_device *device = (rtl8169_device *)cookie; + cpu_status cpu; status_t stat; + int len; - LOG("rtl8169_write()\n"); + TRACE("rtl8169_write() enter\n"); - if (device->closed) - return B_INTERRUPTED; + len = *num_bytes; + if (len > FRAME_SIZE) { + TRACE("rtl8169_write() buffer too large\n"); + return B_ERROR; + } - stat = acquire_sem_etc(device->txFreeSem, 1, B_CAN_INTERRUPT | B_TIMEOUT, TX_TIMEOUT); - if (device->closed) + if (device->closed) { + TRACE("rtl8169_write() interrupted 1\n"); return B_INTERRUPTED; - if (device->blockFlag && stat == B_WOULD_BLOCK) { + } +retry: + stat = acquire_sem_etc(device->txFreeSem, 1, B_CAN_INTERRUPT | B_TIMEOUT, device->nonblocking ? 0 : TX_TIMEOUT); + if (device->closed) { + TRACE("rtl8169_write() interrupted 2\n"); + return B_INTERRUPTED; + } + if (stat == B_WOULD_BLOCK) { + TRACE("rtl8169_write() would block (OK 0 bytes)\n"); *num_bytes = 0; return B_OK; } - if (stat != B_OK) + if (stat == B_TIMED_OUT) { + TRACE("rtl8169_write() timeout\n"); + return B_BUSY; + } + if (stat != B_OK) { + TRACE("rtl8169_write() error\n"); return B_ERROR; + } + if (device->txDesc[device->txNextIndex].stat_len & TX_DESC_OWN) { + ERROR("rtl8169_write() buffer still in use\n"); + goto retry; + } + memcpy(device->txBuf[device->txNextIndex], buffer, len); + cpu = disable_interrupts(); + acquire_spinlock(&device->txSpinlock); + + device->txUsed++; + device->txDesc[device->txNextIndex].stat_len = (device->txDesc[device->txNextIndex].stat_len & RX_DESC_EOR) | TX_DESC_OWN | TX_DESC_FS | TX_DESC_LS | len; + + release_spinlock(&device->txSpinlock); + restore_interrupts(cpu); + + device->txNextIndex = (device->txNextIndex + 1) % TX_DESC_COUNT; + + write8(REG_TPPOLL, read8(REG_TPPOLL) | TPPOLL_NPQ); // set queue polling bit + + TRACE("rtl8169_write() leave\n"); return B_OK; } - status_t rtl8169_control(void *cookie, uint32 op, void *arg, size_t len) { rtl8169_device *device = (rtl8169_device *)cookie; - LOG("rtl8169_control()\n"); - - switch (op) { case ETHER_INIT: - LOG("ETHER_INIT\n"); + TRACE("rtl8169_control() ETHER_INIT\n"); return B_OK; case ETHER_GETADDR: - LOG("ETHER_GETADDR\n"); + TRACE("rtl8169_control() ETHER_GETADDR\n"); memcpy(arg, &device->macaddr, sizeof(device->macaddr)); return B_OK; case ETHER_NONBLOCK: - LOG("ETHER_NON_BLOCK\n"); - device->blockFlag = *(int32 *)arg ? B_TIMEOUT : 0; + if (*(int32 *)arg) { + TRACE("non blocking mode on\n"); + device->nonblocking = true; + /* could be used to unblock pending read and write calls, + * but this doesn't seem to be required + release_sem_etc(device->txFreeSem, 1, B_DO_NOT_RESCHEDULE); + release_sem_etc(device->rxReadySem, 1, B_DO_NOT_RESCHEDULE); + */ + } else { + TRACE("non blocking mode off\n"); + device->nonblocking = false; + } return B_OK; case ETHER_ADDMULTI: - LOG("ETHER_ADDMULTI\n"); + TRACE("rtl8169_control() ETHER_ADDMULTI\n"); break; case ETHER_REMMULTI: - LOG("ETHER_REMMULTI\n"); + TRACE("rtl8169_control() ETHER_REMMULTI\n"); return B_OK; case ETHER_SETPROMISC: - LOG("ETHER_SETPROMISC\n"); + if (*(int32 *)arg) { + TRACE("promiscuous mode on\n"); + write32(REG_RX_CONFIG, read32(REG_RX_CONFIG) | RX_CONFIG_AcceptAllPhys); + write32(0x8, 0xffffffff); // multicast filter + write32(0xc, 0xffffffff); // multicast filter + } else { + TRACE("promiscuous mode off\n"); + write32(REG_RX_CONFIG, read32(REG_RX_CONFIG) & ~RX_CONFIG_AcceptAllPhys); + write32(0x8, 0); // multicast filter + write32(0xc, 0); // multicast filter + } return B_OK; - + case ETHER_GETFRAMESIZE: - LOG("ETHER_GETFRAMESIZE\n"); + TRACE("rtl8169_control() ETHER_GETFRAMESIZE\n"); *(uint32*)arg = FRAME_SIZE; return B_OK; default: - LOG("Invalid command\n"); + TRACE("rtl8169_control() Invalid command\n"); break; } diff --git a/src/add-ons/kernel/drivers/network/rtl8169/device.h b/src/add-ons/kernel/drivers/network/rtl8169/device.h index 9e4aba874f..af256725c7 100644 --- a/src/add-ons/kernel/drivers/network/rtl8169/device.h +++ b/src/add-ons/kernel/drivers/network/rtl8169/device.h @@ -1,13 +1,16 @@ +/* Realtek RTL8169 Family Driver + * Copyright (C) 2004 Marcus Overhagen . All rights reserved. + */ #ifndef __DEVICE_H #define __DEVICE_H #include #include "hardware.h" -#define TX_TIMEOUT 250000 -#define FRAME_SIZE 1536 // must be multiple of 8 -#define TX_DESC_COUNT 42 // must be <= 1024 -#define RX_DESC_COUNT 42 // must be <= 1024 +#define TX_TIMEOUT 6000000 // 6 seconds +#define FRAME_SIZE 1536 // must be multiple of 8 +#define TX_DESC_COUNT 256 // must be <= 1024 +#define RX_DESC_COUNT 256 // must be <= 1024 extern pci_module_info *gPci; @@ -22,21 +25,26 @@ typedef struct { int devId; pci_info * pciInfo; - volatile uint32 blockFlag; + volatile bool nonblocking; volatile bool closed; - sem_id rxReadySem; + spinlock txSpinlock; sem_id txFreeSem; + volatile int32 txNextIndex; // next descriptor that will be used for writing + volatile int32 txIntIndex; // current descriptor that needs be checked + volatile int32 txUsed; + + spinlock rxSpinlock; + sem_id rxReadySem; + volatile int32 rxNextIndex; // next descriptor that will be used for reading + volatile int32 rxIntIndex; // current descriptor that needs be checked + volatile int32 rxFree; - volatile int32 txIndex; - volatile int32 rxIndex; - volatile tx_desc * txDesc; - volatile tx_desc * txPrioDesc; - volatile rx_desc * rxDesc; + volatile buf_desc * txDesc; + volatile buf_desc * rxDesc; area_id txDescArea; - area_id txPrioDescArea; area_id rxDescArea; area_id txBufArea; area_id rxBufArea; @@ -46,13 +54,18 @@ typedef struct { void * regAddr; area_id refArea; - uint8 irq; + uint8 irq; uint8 macaddr[6]; + int mac_version; + int phy_version; } rtl8169_device; -#define REG8(offset) ((volatile uint8 *)((char *)(device->regAddr) + (offset))) -#define REG16(offset) ((volatile uint16 *)((char *)(device->regAddr) + (offset))) -#define REG32(offset) ((volatile uint32 *)((char *)(device->regAddr) + (offset))) +#define read8(offset) (*(volatile uint8 *) ((char *)(device->regAddr) + (offset))) +#define read16(offset) (*(volatile uint16 *)((char *)(device->regAddr) + (offset))) +#define read32(offset) (*(volatile uint32 *)((char *)(device->regAddr) + (offset))) +#define write8(offset, value) (*(volatile uint8 *) ((char *)(device->regAddr) + (offset)) = value) +#define write16(offset, value) (*(volatile uint16 *)((char *)(device->regAddr) + (offset)) = value) +#define write32(offset, value) (*(volatile uint32 *)((char *)(device->regAddr) + (offset)) = value) #endif