lay base for the packet dispatcher

This commit is contained in:
Arisotura 2024-06-12 20:53:42 +02:00
parent 9c3749bfa0
commit 4e2cb3bfcf
10 changed files with 387 additions and 158 deletions

View File

@ -31,6 +31,8 @@ set(SOURCES_QT_SDL
ROMInfoDialog.cpp
RAMInfoDialog.cpp
TitleManagerDialog.cpp
PacketDispatcher.cpp
Net.cpp
Net_PCap.cpp
Net_Slirp.cpp
LocalMP.cpp

View File

@ -50,17 +50,17 @@ struct MPPacketHeader
u64 Timestamp;
};
QMutex MPQueueLock;
MPStatusData MPStatus;
u8* MPPacketQueue = nullptr;
u8* MPReplyQueue = nullptr;
u32 PacketReadOffset[16];
u32 ReplyReadOffset[16];
const u32 kPacketQueueSize = 0x10000;
const u32 kReplyQueueSize = 0x10000;
const u32 kMaxFrameSize = 0x948;
QMutex MPQueueLock;
MPStatusData MPStatus;
u8 MPPacketQueue[kPacketQueueSize];
u8 MPReplyQueue[kReplyQueueSize];
u32 PacketReadOffset[16];
u32 ReplyReadOffset[16];
int RecvTimeout;
int LastHostID;
@ -100,8 +100,6 @@ bool Init()
{
MPQueueLock.lock();
MPPacketQueue = new u8[kPacketQueueSize];
MPReplyQueue = new u8[kReplyQueueSize];
memset(MPPacketQueue, 0, kPacketQueueSize);
memset(MPReplyQueue, 0, kReplyQueueSize);
memset(&MPStatus, 0, sizeof(MPStatus));
@ -127,10 +125,6 @@ bool Init()
void DeInit()
{
delete MPPacketQueue;
delete MPReplyQueue;
MPPacketQueue = nullptr;
MPReplyQueue = nullptr;
}
void SetRecvTimeout(int timeout)

113
src/frontend/qt_sdl/Net.cpp Normal file
View File

@ -0,0 +1,113 @@
/*
Copyright 2016-2023 melonDS team
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdio.h>
#include <string.h>
#include <QMutex>
#include "Net.h"
#include "FIFO.h"
#include "Platform.h"
#include "Config.h"
using namespace melonDS;
namespace Net
{
using Platform::Log;
using Platform::LogLevel;
bool Inited = false;
bool DirectMode;
QMutex RXMutex;
RingBuffer<0x10000> RXBuffer;
bool Init()
{
if (Inited) DeInit();
RXBuffer.Clear();
Config::Table cfg = Config::GetGlobalTable();
DirectMode = cfg.GetBool("LAN.DirectMode");
bool ret = false;
if (DirectMode)
ret = Net_PCap::Init();
else
ret = Net_Slirp::Init();
Inited = ret;
return ret;
}
void DeInit()
{
if (!Inited) return;
if (DirectMode)
Net_PCap::DeInit();
else
Net_Slirp::DeInit();
Inited = false;
}
void RXEnqueue(const void* buf, int len)
{
int totallen = len + 4;
RXMutex.lock();
if (!RXBuffer.CanFit(totallen))
{
RXMutex.unlock();
Log(LogLevel::Warn, "Net: !! NOT ENOUGH SPACE IN RX BUFFER\n");
return;
}
u32 header = (len & 0xFFFF) | (len << 16);
RXBuffer.Write(&header, sizeof(u32));
RXBuffer.Write(buf, len);
RXMutex.unlock();
}
int SendPacket(u8* data, int len)
{
if (DirectMode)
return Net_PCap::SendPacket(data, len);
else
return Net_Slirp::SendPacket(data, len);
}
int RecvPacket(u8* data)
{
if (DirectMode)
Net_PCap::RecvCheck();
else
Net_Slirp::RecvCheck();
// TODO: check MAC
// FIFO header | destination MAC | source MAC | frame type
}
}

40
src/frontend/qt_sdl/Net.h Normal file
View File

@ -0,0 +1,40 @@
/*
Copyright 2016-2023 melonDS team
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef NET_H
#define NET_H
#include "types.h"
#include "Net_PCap.h"
#include "Net_Slirp.h"
namespace Net
{
using namespace melonDS;
bool Init();
void DeInit();
void RXEnqueue(const void* buf, int len);
int SendPacket(u8* data, int len);
int RecvPacket(u8* data);
}
#endif // NET_H

View File

@ -16,21 +16,13 @@
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
// direct LAN interface. Currently powered by libpcap, may change.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pcap/pcap.h>
#include "Wifi.h"
#include "Net_PCap.h"
#include "Net.h"
#include "Config.h"
#include "Platform.h"
#include "main.h"
// REMOVE ME
//extern EmuInstance* testinst;
#ifdef __WIN32__
#include <iphlpapi.h>
#else
@ -57,7 +49,7 @@ using Platform::LogLevel;
#define DECL_PCAP_FUNC(ret, name, args, args2) \
typedef ret (*type_##name) args; \
type_##name ptr_##name = NULL; \
type_##name ptr_##name = nullptr; \
ret name args { return ptr_##name args2; }
DECL_PCAP_FUNC(int, pcap_findalldevs, (pcap_if_t** alldevs, char* errbuf), (alldevs,errbuf))
@ -70,7 +62,7 @@ DECL_PCAP_FUNC(int, pcap_dispatch, (pcap_t* dev, int num, pcap_handler callback,
DECL_PCAP_FUNC(const u_char*, pcap_next, (pcap_t* dev, struct pcap_pkthdr* hdr), (dev,hdr))
namespace LAN_PCap
namespace Net_PCap
{
const char* PCapLibNames[] =
@ -86,14 +78,14 @@ const char* PCapLibNames[] =
"libpcap.so.1",
"libpcap.so",
#endif
NULL
nullptr
};
AdapterData* Adapters = NULL;
AdapterData* Adapters = nullptr;
int NumAdapters = 0;
Platform::DynamicLibrary* PCapLib = NULL;
pcap_t* PCapAdapter = NULL;
Platform::DynamicLibrary* PCapLib = nullptr;
pcap_t* PCapAdapter = nullptr;
AdapterData* PCapAdapterData;
u8 PacketBuffer[2048];
@ -121,7 +113,7 @@ bool TryLoadPCap(Platform::DynamicLibrary *lib)
bool Init(bool open_adapter)
{
PCapAdapter = NULL;
PCapAdapter = nullptr;
PacketLen = 0;
RXNum = 0;
@ -130,7 +122,7 @@ bool Init(bool open_adapter)
// TODO: how to deal with cases where an adapter is unplugged or changes config??
if (!PCapLib)
{
PCapLib = NULL;
PCapLib = nullptr;
for (int i = 0; PCapLibNames[i]; i++)
{
@ -148,7 +140,7 @@ bool Init(bool open_adapter)
break;
}
if (PCapLib == NULL)
if (PCapLib == nullptr)
{
Log(LogLevel::Error, "PCap: init failed\n");
return false;
@ -160,7 +152,7 @@ bool Init(bool open_adapter)
pcap_if_t* alldevs;
ret = pcap_findalldevs(&alldevs, errbuf);
if (ret < 0 || alldevs == NULL)
if (ret < 0 || alldevs == nullptr)
{
Log(LogLevel::Warn, "PCap: no devices available\n");
return false;
@ -200,12 +192,12 @@ bool Init(bool open_adapter)
ULONG bufsize = 16384;
IP_ADAPTER_ADDRESSES* buf = (IP_ADAPTER_ADDRESSES*)HeapAlloc(GetProcessHeap(), 0, bufsize);
ULONG uret = GetAdaptersAddresses(AF_INET, 0, NULL, buf, &bufsize);
ULONG uret = GetAdaptersAddresses(AF_INET, 0, nullptr, buf, &bufsize);
if (uret == ERROR_BUFFER_OVERFLOW)
{
HeapFree(GetProcessHeap(), 0, buf);
buf = (IP_ADAPTER_ADDRESSES*)HeapAlloc(GetProcessHeap(), 0, bufsize);
uret = GetAdaptersAddresses(AF_INET, 0, NULL, buf, &bufsize);
uret = GetAdaptersAddresses(AF_INET, 0, nullptr, buf, &bufsize);
}
if (uret != ERROR_SUCCESS)
{
@ -225,10 +217,10 @@ bool Init(bool open_adapter)
continue;
}
WideCharToMultiByte(CP_UTF8, 0, addr->FriendlyName, 127, adata->FriendlyName, 127, NULL, NULL);
WideCharToMultiByte(CP_UTF8, 0, addr->FriendlyName, 127, adata->FriendlyName, 127, nullptr, nullptr);
adata->FriendlyName[127] = '\0';
WideCharToMultiByte(CP_UTF8, 0, addr->Description, 127, adata->Description, 127, NULL, NULL);
WideCharToMultiByte(CP_UTF8, 0, addr->Description, 127, adata->Description, 127, nullptr, nullptr);
adata->Description[127] = '\0';
if (addr->PhysicalAddressLength != 6)
@ -322,8 +314,8 @@ bool Init(bool open_adapter)
if (PCapAdapter) pcap_close(PCapAdapter);
// open pcap device
//std::string devicename = testinst->getGlobalConfig().GetString("LAN.Device");
std::string devicename = "FIXME";
Config::Table cfg = Config::GetGlobalTable();
std::string devicename = cfg.GetString("LAN.Device");
PCapAdapterData = &Adapters[0];
for (int i = 0; i < NumAdapters; i++)
{
@ -344,7 +336,7 @@ bool Init(bool open_adapter)
if (pcap_setnonblock(PCapAdapter, 1, errbuf) < 0)
{
Log(LogLevel::Error, "PCap: failed to set nonblocking mode\n");
pcap_close(PCapAdapter); PCapAdapter = NULL;
pcap_close(PCapAdapter); PCapAdapter = nullptr;
return false;
}
@ -358,29 +350,23 @@ void DeInit()
if (PCapAdapter)
{
pcap_close(PCapAdapter);
PCapAdapter = NULL;
PCapAdapter = nullptr;
}
Platform::DynamicLibrary_Unload(PCapLib);
PCapLib = NULL;
PCapLib = nullptr;
}
}
void RXCallback(u_char* blarg, const struct pcap_pkthdr* header, const u_char* data)
void RXCallback(u_char* userdata, const struct pcap_pkthdr* header, const u_char* data)
{
while (RXNum > 0);
if (header->len > 2048-64) return;
PacketLen = header->len;
memcpy(PacketBuffer, data, PacketLen);
RXNum = 1;
Net::RXEnqueue(data, header->len);
}
int SendPacket(u8* data, int len)
{
if (PCapAdapter == NULL)
if (PCapAdapter == nullptr)
return 0;
if (len > 2048)
@ -394,21 +380,12 @@ int SendPacket(u8* data, int len)
return len;
}
int RecvPacket(u8* data)
void RecvCheck()
{
if (PCapAdapter == NULL)
return 0;
if (PCapAdapter == nullptr)
return;
int ret = 0;
if (RXNum > 0)
{
memcpy(data, PacketBuffer, PacketLen);
ret = PacketLen;
RXNum = 0;
}
pcap_dispatch(PCapAdapter, 1, RXCallback, NULL);
return ret;
pcap_dispatch(PCapAdapter, 1, RXCallback, nullptr);
}
}

View File

@ -16,12 +16,12 @@
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef LAN_PCAP_H
#define LAN_PCAP_H
#ifndef NET_PCAP_H
#define NET_PCAP_H
#include "types.h"
namespace LAN_PCap
namespace Net_PCap
{
using namespace melonDS;
@ -46,8 +46,8 @@ bool Init(bool open_adapter);
void DeInit();
int SendPacket(u8* data, int len);
int RecvPacket(u8* data);
void RecvCheck();
}
#endif // LAN_PCAP_H
#endif // NET_PCAP_H

View File

@ -16,13 +16,9 @@
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
// indirect LAN interface, powered by BSD sockets.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "Wifi.h"
#include "Net_Slirp.h"
#include "Net.h"
#include "FIFO.h"
#include "Platform.h"
@ -39,7 +35,7 @@
using namespace melonDS;
namespace LAN_Socket
namespace Net_Slirp
{
using Platform::Log;
@ -58,10 +54,6 @@ u32 IPv4ID;
Slirp* Ctx = nullptr;
/*const int FDListMax = 64;
struct pollfd FDList[FDListMax];
int FDListSize;*/
#ifdef __WIN32__
@ -85,23 +77,6 @@ int clock_gettime(int, struct timespec *spec)
#endif // __WIN32__
void RXEnqueue(const void* buf, int len)
{
int alignedlen = (len + 3) & ~3;
int totallen = alignedlen + 4;
if (!RXBuffer.CanFit(totallen >> 2))
{
Log(LogLevel::Warn, "slirp: !! NOT ENOUGH SPACE IN RX BUFFER\n");
return;
}
u32 header = (alignedlen & 0xFFFF) | (len << 16);
RXBuffer.Write(header);
for (int i = 0; i < alignedlen; i += 4)
RXBuffer.Write(((u32*)buf)[i>>2]);
}
ssize_t SlirpCbSendPacket(const void* buf, size_t len, void* opaque)
{
if (len > 2048)
@ -112,7 +87,7 @@ ssize_t SlirpCbSendPacket(const void* buf, size_t len, void* opaque)
Log(LogLevel::Debug, "slirp: response packet of %zu bytes, type %04X\n", len, ntohs(((u16*)buf)[6]));
RXEnqueue(buf, len);
Net::RXEnqueue(buf, len);
return len;
}
@ -145,40 +120,11 @@ void SlirpCbTimerMod(void* timer, int64_t expire_time, void* opaque)
void SlirpCbRegisterPollFD(int fd, void* opaque)
{
Log(LogLevel::Debug, "Slirp: register poll FD %d\n", fd);
/*if (FDListSize >= FDListMax)
{
printf("!! SLIRP FD LIST FULL\n");
return;
}
for (int i = 0; i < FDListSize; i++)
{
if (FDList[i].fd == fd) return;
}
FDList[FDListSize].fd = fd;
FDListSize++;*/
}
void SlirpCbUnregisterPollFD(int fd, void* opaque)
{
Log(LogLevel::Debug, "Slirp: unregister poll FD %d\n", fd);
/*if (FDListSize < 1)
{
printf("!! SLIRP FD LIST EMPTY\n");
return;
}
for (int i = 0; i < FDListSize; i++)
{
if (FDList[i].fd == fd)
{
FDListSize--;
FDList[i] = FDList[FDListSize];
}
}*/
}
void SlirpCbNotify(void* opaque)
@ -203,9 +149,6 @@ bool Init()
{
IPv4ID = 0;
//FDListSize = 0;
//memset(FDList, 0, sizeof(FDList));
SlirpConfig cfg;
memset(&cfg, 0, sizeof(cfg));
cfg.version = 1;
@ -425,7 +368,7 @@ void HandleDNSFrame(u8* data, int len)
if (framelen & 1) { *out++ = 0; framelen++; }
FinishUDPFrame(resp, framelen);
RXEnqueue(resp, framelen);
Net::RXEnqueue(resp, framelen);
}
int SendPacket(u8* data, int len)
@ -472,8 +415,6 @@ int SlirpCbAddPoll(int fd, int events, void* opaque)
int idx = PollListSize++;
//printf("Slirp: add poll: fd=%d, idx=%d, events=%08X\n", fd, idx, events);
u16 evt = 0;
if (events & SLIRP_POLL_IN) evt |= POLLIN;
@ -497,8 +438,6 @@ int SlirpCbGetREvents(int idx, void* opaque)
if (idx < 0 || idx >= PollListSize)
return 0;
//printf("Slirp: get revents, idx=%d, res=%04X\n", idx, FDList[idx].revents);
u16 evt = PollList[idx].revents;
int ret = 0;
@ -511,11 +450,9 @@ int SlirpCbGetREvents(int idx, void* opaque)
return ret;
}
int RecvPacket(u8* data)
void RecvCheck()
{
if (!Ctx) return 0;
int ret = 0;
if (!Ctx) return;
//if (PollListSize > 0)
{
@ -525,19 +462,6 @@ int RecvPacket(u8* data)
int res = poll(PollList, PollListSize, timeout);
slirp_pollfds_poll(Ctx, res<0, SlirpCbGetREvents, nullptr);
}
if (!RXBuffer.IsEmpty())
{
u32 header = RXBuffer.Read();
u32 len = header & 0xFFFF;
for (int i = 0; i < len; i += 4)
((u32*)data)[i>>2] = RXBuffer.Read();
ret = header >> 16;
}
return ret;
}
}

View File

@ -16,24 +16,21 @@
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef LAN_SOCKET_H
#define LAN_SOCKET_H
#ifndef NET_SLIRP_H
#define NET_SLIRP_H
#include "types.h"
namespace LAN_Socket
namespace Net_Slirp
{
using namespace melonDS;
//
bool Init();
void DeInit();
int SendPacket(u8* data, int len);
int RecvPacket(u8* data);
void RecvCheck();
}
#endif // LAN_SOCKET_H
#endif // NET_SLIRP_H

View File

@ -0,0 +1,136 @@
/*
Copyright 2016-2023 melonDS team
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include "PacketDispatcher.h"
using namespace melonDS;
struct PacketHeader
{
u32 magic;
u32 senderID;
u32 headerLength;
u32 dataLength;
};
const u32 kPacketMagic = 0x4B504C4D;
PacketDispatcher::PacketDispatcher()
{
instanceMask = 0;
memset(packetQueues, 0, sizeof(packetQueues));
}
PacketDispatcher::~PacketDispatcher()
{
//
}
void PacketDispatcher::registerInstance(int inst)
{
mutex.lock();
instanceMask |= (1 << inst);
packetQueues[inst] = new PacketQueue();
mutex.unlock();
}
void PacketDispatcher::unregisterInstance(int inst)
{
mutex.lock();
instanceMask &= ~(1 << inst);
delete packetQueues[inst];
mutex.unlock();
}
void PacketDispatcher::sendPacket(const void* header, int headerlen, const void* data, int datalen, int sender, u16 recv_mask)
{
if (!header) headerlen = 0;
if (!data) datalen = 0;
if ((!headerlen) && (!datalen)) return;
if ((sizeof(PacketHeader) + headerlen + datalen) >= 0x8000) return;
if (sender < 0 || sender > 15) return;
recv_mask &= instanceMask;
recv_mask &= ~(1 << sender);
if (!recv_mask) return;
PacketHeader phdr;
phdr.magic = kPacketMagic;
phdr.senderID = sender;
phdr.headerLength = headerlen;
phdr.dataLength = datalen;
mutex.lock();
for (int i = 0; i < 16; i++)
{
if (!(recv_mask & (1 << i)))
continue;
PacketQueue* queue = packetQueues[i];
queue->Write(&phdr, sizeof(phdr));
if (headerlen) queue->Write(header, headerlen);
if (datalen) queue->Write(data, datalen);
}
mutex.unlock();
}
bool PacketDispatcher::recvPacket(void *header, int *headerlen, void *data, int *datalen, int receiver)
{
if ((!header) && (!data)) return false;
if (receiver < 0 || receiver > 15) return false;
mutex.lock();
PacketQueue* queue = packetQueues[receiver];
PacketHeader phdr;
if (!queue->Read(&phdr, sizeof(phdr)))
{
mutex.unlock();
return false;
}
if (phdr.magic != kPacketMagic)
{
mutex.unlock();
return false;
}
if (phdr.headerLength)
{
if (headerlen) *headerlen = phdr.headerLength;
if (header) queue->Read(header, phdr.headerLength);
else queue->Skip(phdr.headerLength);
}
if (phdr.dataLength)
{
if (datalen) *datalen = phdr.dataLength;
if (data) queue->Read(data, phdr.dataLength);
else queue->Skip(phdr.dataLength);
}
mutex.unlock();
return true;
}

View File

@ -0,0 +1,46 @@
/*
Copyright 2016-2023 melonDS team
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef PACKETDISPATCHER_H
#define PACKETDISPATCHER_H
#include <QMutex>
#include "types.h"
#include "FIFO.h"
using PacketQueue = melonDS::RingBuffer<0x8000>;
class PacketDispatcher
{
public:
PacketDispatcher();
~PacketDispatcher();
void registerInstance(int inst);
void unregisterInstance(int inst);
void sendPacket(const void* header, int headerlen, const void* data, int datalen, int sender, melonDS::u16 recv_mask);
bool recvPacket(void* header, int* headerlen, void* data, int* datalen, int receiver);
private:
QMutex mutex;
melonDS::u16 instanceMask;
PacketQueue* packetQueues[16];
};
#endif // PACKETDISPATCHER_H