#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);
}
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