xaizek / tos (License: GPLv3 only) (since 2018-12-07)
This is an alternative version of sources presented as part of Write Your Own OS video tutorial by Viktor Engelmann.
<root> / src / drv / rtl8139.cpp (91fa3d83786785b93bf66b6fb89e65ccc849b14f) (4,295B) (mode 100644) [raw]
#include "rtl8139.hpp"

#include <cstdint>

#include <algorithm>

#include "hwcomm/PCI.hpp"
#include "hwcomm/interrupts.hpp"
#include "utils/conv.hpp"

using namespace drv;

using hwcomm::Port32;

rtl8139::rtl8139(hwcomm::PCIDeviceDescr &dev,
                 hwcomm::InterruptManager &interrupts)
    : InterruptHandler(dev.interrupt + interrupts.getIRQBase()),
      macLowPort(dev.portBase + 0x00), macHighPort(dev.portBase + 0x04),
      marLowPort(dev.portBase + 0x08), marHighPort(dev.portBase + 0x0c),
      rbstartPort(dev.portBase + 0x30),
      cmdPort(dev.portBase + 0x37),
      caprPort(dev.portBase + 0x38),
      imrPort(dev.portBase + 0x3c),
      isrPort(dev.portBase + 0x3e),
      tcrPort(dev.portBase + 0x44),
      rcrPort(dev.portBase + 0x44),
      cfgPort(dev.portBase + 0x52),
      tsdPort { Port32(dev.portBase + 0x10), Port32(dev.portBase + 0x14),
                Port32(dev.portBase + 0x18), Port32(dev.portBase + 0x1c) },
      tsadPort { Port32(dev.portBase + 0x20), Port32(dev.portBase + 0x24),
                 Port32(dev.portBase + 0x28), Port32(dev.portBase + 0x2c) },
      currentSendDescr(0)
{
    dev.enableBusMaster();

    // Power on the device.
    cfgPort.write(0);

    reset();

    mac = macHighPort.read();
    mac = (mac << 32 | macLowPort.read());

    // Setup receive buffer.
    auto addr = [](void *ptr) {
        return reinterpret_cast<std::uint32_t>(ptr);
    };
    auto align = [&addr](void *ptr) {
        auto mask = ~static_cast<std::uint32_t>(0xf);
        return ((addr(ptr) + 15) & mask);
    };
    auto alignP = [&align](void *ptr) {
        return reinterpret_cast<std::uint8_t *>(align(ptr));
    };
    recvBuffer = alignP(recvBufferStorage);
    rbstartPort.write(addr(recvBuffer));

    // Setup send buffers.
    for (int i = 0; i < 4; ++i) {
        sendBuffer[i] = alignP(sendBufferStorage[i]);
        tsadPort[i].write(addr(sendBuffer[i]));
    }

    // Initialize IMR to fire IRQ after successful or failed receive and sent.
    imrPort.write(0x0f);

    // Accept everything + "overflow" receiving buffer.
    rcrPort.write(0xf | (1 << 7));
}

void
rtl8139::activate()
{
    // Enable receiving and transferring packets.
    cmdPort.write(0x0c);
}

int
rtl8139::reset()
{
    cmdPort.write(0x10);
    while (cmdPort.read() & 0x10) {
        // Wait while reset is in progress.
    }
    return 0;
}

const char *
rtl8139::getName() const
{
    return "rtl8139";
}

void
rtl8139::send(span<const std::uint8_t> msg)
{
    if (msg.size() > 1792) {
        msg = msg.first(1792);
    }

    for (int i = 0; i < msg.size(); ++i) {
        sendBuffer[currentSendDescr][i] = msg[i];
    }

    tsdPort[currentSendDescr].write(msg.size());

    currentSendDescr = (currentSendDescr + 1) % 4;
}

void
rtl8139::receive()
{
}

std::uint32_t
rtl8139::handleInterrupt(std::uint32_t esp)
{
    const std::uint16_t isr = isrPort.read();
    if (isr & 0x08) {
        isrPort.write(0x08);
    }
    if (isr & 0x04) {
        isrPort.write(0x04);
    }
    if (isr & 0x02) {
        isrPort.write(0x02);
    }
    if (isr & 0x01) {
        while (!(cmdPort.read() & 0x01)) {
            received();
        }
        isrPort.write(0x01);
    }

    return esp;
}

void
rtl8139::received()
{
    // XXX: wrapping might not work properly, needs testing.
    // XXX: this weird +/- 0x10 for caprPort seems to be necessary, but can't
    //      find why.

    std::uint16_t offset = caprPort.read() + 0x10;
    std::uint8_t *start = recvBuffer + offset;

    std::uint16_t header = (static_cast<std::uint16_t>(start[1]) << 8)
                         | start[0];
    std::uint16_t length = (static_cast<std::uint16_t>(start[3]) << 8)
                         | start[2];

    static_cast<void>(header);

    // Drop checksum if present.
    const std::uint16_t payloadSize = (length > 64 ? length - 4 : length);

    if (handler != nullptr) {
        span<std::uint8_t> payload(start + 4, payloadSize);
        if (handler->onRawDataReceived(payload)) {
            send(payload);
        }
    }

    offset = (offset + length + 4 + 3) & ~0x3;
    offset %= recvBufferStorage + sizeof(recvBufferStorage) - recvBuffer;
    caprPort.write(offset - 0x10);
}

NetOrder<std::uint64_t>
rtl8139::getMAC() const
{
    return netOrder(mac);
}
Hints

Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://code.reversed.top/user/xaizek/tos

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@code.reversed.top/user/xaizek/tos

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a pull request:
... clone the repository ...
... make some changes and some commits ...
git push origin master