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 / amd_am79c973.cpp (a046b38a82beef80ba62f898082aa08055ffe00a) (5,921B) (mode 100644) [raw]
#include "drv/amd_am79c973.hpp"

#include <cstdint>

#include <new>

#include "hwcomm/PCI.hpp"
#include "print.hpp"

using namespace drv;
using namespace hwcomm;

amd_am79c973::amd_am79c973(const PCIDeviceDescr &dev,
                           InterruptManager &interrupts)
    : InterruptHandler(dev.interrupt + interrupts.getIRQBase()),
      macAddress0Port(dev.portBase),
      macAddress2Port(dev.portBase + 0x02),
      macAddress4Port(dev.portBase + 0x04),
      registerDataPort(dev.portBase + 0x10),
      registerAddressPort(dev.portBase + 0x12),
      resetPort(dev.portBase + 0x14),
      busControlRegisterDataPort(dev.portBase + 0x16)
{
    currentSendBuffer = 0;
    currentRecvBuffer = 0;

    std::uint64_t MAC0 = macAddress0Port.read() % 256;
    std::uint64_t MAC1 = macAddress0Port.read() / 256;
    std::uint64_t MAC2 = macAddress2Port.read() % 256;
    std::uint64_t MAC3 = macAddress2Port.read() / 256;
    std::uint64_t MAC4 = macAddress4Port.read() % 256;
    std::uint64_t MAC5 = macAddress4Port.read() / 256;

    std::uint64_t MAC = MAC5 << 40
                      | MAC4 << 32
                      | MAC3 << 24
                      | MAC2 << 16
                      | MAC1 << 8
                      | MAC0;

    // 32 bit mode.
    registerAddressPort.write(20);
    busControlRegisterDataPort.write(0x102);

    // STOP reset.
    registerAddressPort.write(0);
    registerDataPort.write(0x04);

    initBlock.mode = 0x0000; // Promiscuous mode = false.
    initBlock.reserved1 = 0;
    initBlock.numSendBuffers = 3;
    initBlock.reserved2 = 0;
    initBlock.numRecvBuffers = 3;
    initBlock.physicalAddress = MAC;
    initBlock.reserved3 = 0;
    initBlock.logicalAddress = 0;

    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<void *>(align(ptr));
    };

    sendBufferDescr = new(alignP(&sendBufferDescrMem[0])) BufferDescriptor[8];
    initBlock.sendBufferDescrAddress = addr(sendBufferDescr);

    recvBufferDescr = new(alignP(&recvBufferDescrMem[0])) BufferDescriptor[8];
    initBlock.recvBufferDescrAddress = addr(recvBufferDescr);

    for (std::uint8_t i = 0; i < 8; ++i) {
        sendBufferDescr[i].address = align(&sendBuffers[i]);
        sendBufferDescr[i].flags = 0x7ff
                                 | 0xf000;
        sendBufferDescr[i].flags2 = 0;
        sendBufferDescr[i].avail = 0;

        recvBufferDescr[i].address = align(&recvBuffers[i]);
        recvBufferDescr[i].flags = 0xf7ff
                                 | 0x80000000;
        recvBufferDescr[i].flags2 = 0;
        sendBufferDescr[i].avail = 0;
    }

    registerAddressPort.write(1);
    registerDataPort.write(addr(&initBlock) & 0xffff);
    registerAddressPort.write(2);
    registerDataPort.write((addr(&initBlock) >> 16) & 0xffff);
}

void
amd_am79c973::activate()
{
    registerAddressPort.write(0);
    registerDataPort.write(0x41);

    registerAddressPort.write(4);
    std::uint32_t temp = registerDataPort.read();
    registerAddressPort.write(4);
    registerDataPort.write(temp | 0xC00);

    registerAddressPort.write(0);
    registerDataPort.write(0x42);
}

int
amd_am79c973::reset()
{
    resetPort.read();
    resetPort.write(0);
    return 10;
}

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

std::uint32_t
amd_am79c973::handleInterrupt(std::uint32_t esp)
{
    kprint("INTERRUPT FROM AMD am79c973\n");

    registerAddressPort.write(0);
    std::uint32_t temp = registerDataPort.read();

    if ((temp & 0x8000) == 0x8000) kprint("AMD am79c973 ERROR\n");
    if ((temp & 0x2000) == 0x2000) kprint("AMD am79c973 COLLISION ERROR\n");
    if ((temp & 0x1000) == 0x1000) kprint("AMD am79c973 MISSED FRAME\n");
    if ((temp & 0x0800) == 0x0800) kprint("AMD am79c973 MEMORY ERROR\n");
    if ((temp & 0x0400) == 0x0400) receive();
    if ((temp & 0x0200) == 0x0200) kprint("AMD am79c973 DATA SENT\n");

    // Acknowledge.
    registerAddressPort.write(0);
    registerDataPort.write(temp);

    if((temp & 0x0100) == 0x0100) kprint("AMD am79c973 INIT DONE\n");

    return esp;
}

void
amd_am79c973::send(span<const std::uint8_t> msg)
{
    BufferDescriptor &descr = sendBufferDescr[currentSendBuffer];
    currentSendBuffer = (currentSendBuffer + 1) % 8;

    if (msg.size() > 1518) {
        msg = msg.first(1518);
    }

    auto dst = reinterpret_cast<std::uint8_t *>(descr.address);
    for (std::uint8_t b : msg) {
        *dst++ = b;
    }

    descr.avail = 0;
    descr.flags2 = 0;
    descr.flags = 0x8300f000
                | (static_cast<std::uint16_t>((-msg.size()) & 0xfff));

    registerAddressPort.write(0);
    registerDataPort.write(0x48);
}

void
amd_am79c973::receive()
{
    kprint("AMD am79c973 DATA RECEIVED\n");

    for (; (recvBufferDescr[currentRecvBuffer].flags & 0x80000000) == 0;
           currentRecvBuffer = (currentRecvBuffer + 1) % 8) {
        BufferDescriptor &descr = recvBufferDescr[currentRecvBuffer];
        if (!(descr.flags & 0x40000000) &&
            (descr.flags & 0x03000000) == 0x03000000)
        {
            std::uint32_t size = descr.flags & 0xFFF;
            if (size > 64) {
                // Drop checksum.
                size -= 4;
            }

            auto buffer = reinterpret_cast<std::uint8_t *>(descr.address);
            if (handler != nullptr) {
                span<std::uint8_t> payload(buffer, size);
                if (handler->onRawDataReceived(payload)) {
                    send(payload);
                }
            }
        }

        descr.flags2 = 0;
        descr.flags = 0x8000f7ff;
    }
}

NetOrder<std::uint64_t>
amd_am79c973::getMAC() const
{
    return netOrder(initBlock.physicalAddress);
}
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