751 lines
21 KiB
C++
751 lines
21 KiB
C++
/*
|
|
Copyright 2023 flyinghead
|
|
|
|
This file is part of Flycast.
|
|
|
|
Flycast 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 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Flycast 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 Flycast. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "netdimm.h"
|
|
#include "naomi.h"
|
|
#include "hw/holly/holly_intc.h"
|
|
#include "hw/holly/sb.h"
|
|
#include "hw/sh4/sh4_sched.h"
|
|
#include "hw/mem/addrspace.h"
|
|
#include "network/net_platform.h"
|
|
#include "serialize.h"
|
|
#include "naomi_roms.h"
|
|
#include "naomi_regs.h"
|
|
|
|
//#define HTTP_TRACE
|
|
|
|
const char *SERVER_NAME = "vfnet.flyca.st";
|
|
|
|
NetDimm::NetDimm(u32 size) : GDCartridge(size)
|
|
{
|
|
if (serverIp == 0)
|
|
{
|
|
hostent *hp = gethostbyname(SERVER_NAME);
|
|
if (hp != nullptr && hp->h_length > 0) {
|
|
memcpy(&serverIp, hp->h_addr_list[0], sizeof(serverIp));
|
|
NOTICE_LOG(NAOMI, "%s IP is %x", SERVER_NAME, serverIp);
|
|
}
|
|
}
|
|
}
|
|
|
|
void NetDimm::Init(LoadProgress *progress, std::vector<u8> *digest)
|
|
{
|
|
GDCartridge::Init(progress, digest);
|
|
dimmBufferOffset = dimm_data_size - 16_MB;
|
|
finalTuned = strcmp(game->name, "vf4tuned") == 0;
|
|
}
|
|
|
|
bool NetDimm::Write(u32 offset, u32 size, u32 data)
|
|
{
|
|
// u8 b0 = data;
|
|
// u8 b1 = data >> 8;
|
|
// INFO_LOG(NAOMI, "Write<%d>%x: %x %c %c", size, offset, data, b0 == 0 ? ' ' : b0, b1 == 0 ? ' ' : b1);
|
|
if (dimm_data != nullptr)
|
|
{
|
|
u32 addr = offset & (dimm_data_size - 1);
|
|
memcpy(&dimm_data[addr], &data, std::min(size, dimm_data_size - addr));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int NetDimm::schedCallback()
|
|
{
|
|
fd_set readFds {};
|
|
fd_set writeFds {};
|
|
fd_set exceptFds {};
|
|
int nfds = -1;
|
|
for (Socket& socket : sockets)
|
|
{
|
|
if (socket.connecting || socket.sending)
|
|
{
|
|
FD_SET(socket.fd, &writeFds);
|
|
FD_SET(socket.fd, &exceptFds);
|
|
nfds = std::max(nfds, (int)socket.fd);
|
|
}
|
|
if (socket.receiving)
|
|
{
|
|
FD_SET(socket.fd, &readFds);
|
|
FD_SET(socket.fd, &exceptFds);
|
|
nfds = std::max(nfds, (int)socket.fd);
|
|
}
|
|
}
|
|
if (nfds > -1)
|
|
{
|
|
if (SB_ISTEXT & 8) // holly_EXP_PCI
|
|
return POLL_CYCLES;
|
|
|
|
timeval tv {};
|
|
int rc = select(nfds + 1, &readFds, &writeFds, &exceptFds, &tv);
|
|
|
|
for (Socket& socket : sockets)
|
|
{
|
|
if (socket.connecting)
|
|
{
|
|
if (rc > 0)
|
|
{
|
|
int so_error;
|
|
socklen_t len = sizeof(so_error);
|
|
|
|
getsockopt(socket.fd, SOL_SOCKET, SO_ERROR, (char *)&so_error, &len);
|
|
if (so_error != L_EINPROGRESS && so_error != L_EWOULDBLOCK)
|
|
{
|
|
INFO_LOG(NAOMI, "connect(%d) completed -> %d", socket.fd, so_error);
|
|
socket.connecting = false;
|
|
socket.lastError = so_error;
|
|
returnToNaomi(so_error != 0, &socket - &sockets[0] + 1, so_error);
|
|
break;
|
|
}
|
|
}
|
|
if (socket.connectTimeout > 0 && socket.connectTime + socket.connectTimeout <= sh4_sched_now64())
|
|
{
|
|
WARN_LOG(NAOMI, "connect(%d) timeout", socket.fd);
|
|
socket.connecting = false;
|
|
socket.lastError = ECONNREFUSED;
|
|
returnToNaomi(true, &socket - &sockets[0] + 1, ECONNREFUSED); // error code?
|
|
break;
|
|
}
|
|
}
|
|
else if (socket.receiving)
|
|
{
|
|
if (rc > 0)
|
|
{
|
|
rc = recv(socket.fd, (char *)socket.recvData, socket.recvLen, 0);
|
|
if (rc == -1)
|
|
{
|
|
int error = get_last_error();
|
|
if (error != L_EAGAIN && error != L_EWOULDBLOCK)
|
|
{
|
|
socket.lastError = get_last_error();
|
|
socket.receiving = false;
|
|
}
|
|
}
|
|
#ifdef HTTP_TRACE
|
|
else if (rc > 0)
|
|
{
|
|
fwrite(socket.recvData, 1, rc, stdout);
|
|
fflush(stdout);
|
|
}
|
|
#endif
|
|
INFO_LOG(NAOMI, "recv(%d, %d) -> %d", (int)(&socket - &sockets[0] + 1), socket.recvLen, rc);
|
|
if (rc >= 0)
|
|
socket.receiving = false;
|
|
if (!socket.receiving)
|
|
{
|
|
returnToNaomi(rc == -1, &socket - &sockets[0] + 1, rc);
|
|
break;
|
|
}
|
|
}
|
|
if (socket.recvTimeout > 0 && socket.recvTime + socket.recvTimeout <= sh4_sched_now64())
|
|
{
|
|
WARN_LOG(NAOMI, "recv(%d) timeout", socket.fd);
|
|
socket.receiving = false;
|
|
socket.lastError = ETIMEDOUT;
|
|
returnToNaomi(true, &socket - &sockets[0] + 1, ETIMEDOUT); // error code?
|
|
break;
|
|
}
|
|
}
|
|
else if (socket.sending)
|
|
{
|
|
if (rc > 0)
|
|
{
|
|
rc = send(socket.fd, (const char *)socket.sendData, socket.sendLen, 0);
|
|
if (rc == -1)
|
|
{
|
|
int error = get_last_error();
|
|
if (error != L_EAGAIN && error != L_EWOULDBLOCK)
|
|
{
|
|
socket.lastError = get_last_error();
|
|
socket.sending = false;
|
|
}
|
|
}
|
|
#ifdef HTTP_TRACE
|
|
else if (rc > 0)
|
|
{
|
|
fwrite(socket.sendData, 1, rc, stdout);
|
|
fflush(stdout);
|
|
}
|
|
#endif
|
|
INFO_LOG(NAOMI, "send(%d, %d) -> %d", (int)(&socket - &sockets[0] + 1), socket.sendLen, rc);
|
|
if (rc >= 0)
|
|
socket.sending = false;
|
|
if (!socket.sending)
|
|
{
|
|
returnToNaomi(rc == -1, &socket - &sockets[0] + 1, rc);
|
|
break;
|
|
}
|
|
}
|
|
if (socket.sendTimeout > 0 && socket.sendTime + socket.sendTimeout <= sh4_sched_now64())
|
|
{
|
|
WARN_LOG(NAOMI, "send(%d) timeout", socket.fd);
|
|
socket.sending = false;
|
|
socket.lastError = ETIMEDOUT;
|
|
returnToNaomi(true, &socket - &sockets[0] + 1, ETIMEDOUT); // error code?
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return isBusy() ? POLL_CYCLES : SH4_MAIN_CLOCK;
|
|
}
|
|
if (SB_ISTEXT & 8) // holly_EXP_PCI
|
|
return SH4_MAIN_CLOCK;
|
|
// if (dnsInProgress)
|
|
// {
|
|
// NOTICE_LOG(NAOMI, "getIpByDns completed");
|
|
// returnToNaomi(false, 0, serverIp);
|
|
// dnsInProgress = false;
|
|
// return SH4_MAIN_CLOCK;
|
|
// }
|
|
|
|
// regularly peek the test request address
|
|
peek<u32>(0xc01fc08);
|
|
asic_RaiseInterrupt(holly_EXP_PCI);
|
|
|
|
u32 testRequest = addrspace::read32(0xc01fc08);
|
|
if (testRequest & 1)
|
|
{
|
|
// bios dimm test
|
|
addrspace::write32(0xc01fc08, testRequest & ~1);
|
|
bool isMem;
|
|
char *p = (char *)addrspace::writeConst(0xc01fd00, isMem, 4);
|
|
strcpy(p, "CHECKING DIMM BD");
|
|
p = (char *)addrspace::writeConst(0xc01fd10, isMem, 4);
|
|
strcpy(p, "DIMM0 - GOOD");
|
|
p = (char *)addrspace::writeConst(0xc01fd20, isMem, 4);
|
|
strcpy(p, "DIMM1 - GOOD");
|
|
p = (char *)addrspace::writeConst(0xc01fd30, isMem, 4);
|
|
strcpy(p, "--- COMPLETED---");
|
|
addrspace::write32(0xc01fc0c, 0x0317a264);
|
|
}
|
|
else if (testRequest & 0x40)
|
|
{
|
|
// when entering vf4 test mode
|
|
addrspace::write32(0xc01fc08, testRequest & ~0x40);
|
|
addrspace::write32(0xc01fc60, htonl(0xc0a80101)); // FIXME ip address (192.168.1.1)
|
|
addrspace::write32(0xc01fc0c, 0x03170264);
|
|
INFO_LOG(NAOMI, "TEST REQUEST %x", testRequest);
|
|
}
|
|
else if (testRequest & 0x400)
|
|
{
|
|
// when entering vf4 test mode
|
|
addrspace::write32(0xc01fc08, testRequest & ~0x400);
|
|
addrspace::write32(0xc01fc70, 0x08080808); // FIXME dns2??? we might be off by one; and this would be dns1?
|
|
addrspace::write32(0xc01fc0c, 0x03170264);
|
|
INFO_LOG(NAOMI, "TEST REQUEST %x", testRequest);
|
|
}
|
|
else if (testRequest & 0x10000)
|
|
{
|
|
// bios network settings
|
|
addrspace::write32(0xc01fc08, testRequest & ~0x10000);
|
|
// TODO save to PIC?
|
|
addrspace::write32(0xc01fc0c, 0x03170264);
|
|
}
|
|
else if (testRequest & 0x20000)
|
|
{
|
|
// network test
|
|
addrspace::write32(0xc01fc08, testRequest & ~0x20000);
|
|
bool isMem;
|
|
char *p = (char *)addrspace::writeConst(0xc01fd00, isMem, 4);
|
|
strcpy(p, "CHECKING NETWORK");
|
|
p = (char *)addrspace::writeConst(0xc01fd10, isMem, 4);
|
|
strcpy(p, "PRETENDING... :P");
|
|
p = (char *)addrspace::writeConst(0xc01fd20, isMem, 4);
|
|
strcpy(p, "--- COMPLETED---");
|
|
addrspace::write32(0xc01fc0c, 0x03170264);
|
|
}
|
|
else if (testRequest != 0)
|
|
{
|
|
addrspace::write32(0xc01fc08, 0);
|
|
addrspace::write32(0xc01fc0c, 0x03170100);
|
|
INFO_LOG(NAOMI, "TEST REQUEST %x", testRequest);
|
|
}
|
|
|
|
return SH4_MAIN_CLOCK;
|
|
}
|
|
|
|
void NetDimm::systemCmd(int cmd)
|
|
{
|
|
switch (cmd)
|
|
{
|
|
case 0xf: // startup
|
|
NOTICE_LOG(NAOMI, "NetDIMM startup");
|
|
// bit 16,17: dimm0 size (none, 128, 256, 512)
|
|
// bit 18,19: dimm1 size
|
|
// bit 28: network enabled (network settings appear in bios menu)
|
|
// bit 29: set
|
|
// bit 30: gd-rom connected
|
|
// bit 31: mobile/ppp network?
|
|
// (| 30, 70, F0, 1F0, 3F0, 7F0)
|
|
// | offset >> 20 (dimm buffers offset @ size - 16MB)
|
|
// offset = (64MB << 0-5) - 16MB
|
|
// vf4 forces this value to 0f000000 (256MB) if != 1f000000 (512MB)
|
|
if (dimm_data_size == 512_MB)
|
|
addrspace::write32(0xc01fc04, (3 << 16) | 0x70000000 | (dimmBufferOffset >> 20)); // dimm board config 1 x 512 MB
|
|
else if (dimm_data_size == 256_MB)
|
|
addrspace::write32(0xc01fc04, (2 << 16) | 0x70000000 | (dimmBufferOffset >> 20)); // dimm board config 1 x 256 MB
|
|
else if (dimm_data_size == 128_MB)
|
|
addrspace::write32(0xc01fc04, (1 << 16) | 0x70000000 | (dimmBufferOffset >> 20)); // dimm board config 1 x 128 MB
|
|
else
|
|
die("Unsupported dimm mem size");
|
|
addrspace::write32(0xc01fc0c, 0x3170000 | 0x264); // fw version | 100/264/364?
|
|
addrspace::write32(0xc01fc10, 0);
|
|
// additional pokes (initPoke_maybe)
|
|
addrspace::write32(0xc01fc14, 1);
|
|
// new in 3.17
|
|
addrspace::write32(0xc01fc20, 0x78000);
|
|
addrspace::write32(0xc01fc24, 0x3e000a);
|
|
addrspace::write32(0xc01fc28, 0x18077f);
|
|
addrspace::write32(0xc01fc2c, 0x10014);
|
|
// DIMM board serial Id
|
|
{
|
|
const u32 *serial = (u32 *)(getGameSerialId() + 0x20); // get only the serial id
|
|
addrspace::write32(0xc01fc40, *serial++);
|
|
addrspace::write32(0xc01fc44, *serial++);
|
|
addrspace::write32(0xc01fc48, *serial++);
|
|
addrspace::write32(0xc01fc4c, *serial++);
|
|
}
|
|
addrspace::write32(0xc01fc18, 0x10002); // net mode (2 or 4 is mobile), bit 16 dhcp?
|
|
// network order
|
|
addrspace::write32(0xc01fc60, htonl(0xc0a80101)); // ip address (192.168.1.1)
|
|
addrspace::write32(0xc01fc64, htonl(0xffffff00)); // netmask
|
|
addrspace::write32(0xc01fc68, htonl(0xc0a801fe)); // gateway 192.168.1.254
|
|
addrspace::write32(0xc01fc6c, htonl(0xc0a801fe)); // dns1
|
|
addrspace::write32(0xc01fc70, htonl(0x08080808)); // dns2
|
|
addrspace::write32(0xc01fc74, 0); // ?
|
|
addrspace::write32(0xc01fc78, 0); // ?
|
|
addrspace::write32(0xc01fc7c, 0); // ppp ip addr
|
|
addrspace::write32(0xc01fc80, 0); // ppp param
|
|
addrspace::write32(0xc01fc84, 0); // ?
|
|
addrspace::write32(0xc01fc88, 0); // ?
|
|
addrspace::write32(0xc01fc8c, 0); // ?
|
|
addrspace::write32(0xc01fc90, 0); // ?
|
|
addrspace::write32(0xc01fc94, 0); // ?
|
|
// SET_BASE_ADDRESS(0c000000, 0)
|
|
dimm_command = 0x8600;
|
|
dimm_offsetl = 0;
|
|
dimm_parameterl = 0;
|
|
dimm_parameterh = 0x0c00;
|
|
asic_RaiseInterrupt(holly_EXP_PCI);
|
|
sh4_sched_request(schedId, SH4_MAIN_CLOCK);
|
|
|
|
break;
|
|
|
|
case 0: // nop
|
|
case 1: // control read
|
|
case 3: // set base address
|
|
case 4: // peek8
|
|
case 5: // peek16
|
|
case 6: // peek32
|
|
case 8: // poke8
|
|
case 9: // poke16
|
|
case 10: // poke32
|
|
// These are callbacks from naomi
|
|
INFO_LOG(NAOMI, "System callback command %x", cmd);
|
|
break;
|
|
|
|
default:
|
|
WARN_LOG(NAOMI, "Unknown system command %x", cmd);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void NetDimm::netCmd(int cmd)
|
|
{
|
|
u32 *buffer = (u32 *)&dimm_data[dimmBufferOffset + 0x800000 + 0x1000 * (dimm_command & 0xff)];
|
|
cmd = buffer[0];
|
|
switch (cmd)
|
|
{
|
|
case 0: // returnToNaomiRawCmd
|
|
WARN_LOG(NAOMI, "netdimm: returnToNaomiRawCmd not implemented");
|
|
returnToNaomi(true, 0, -1);
|
|
break;
|
|
case 1: // accept
|
|
WARN_LOG(NAOMI, "netdimm: accept not implemented");
|
|
returnToNaomi(true, buffer[1], -1);
|
|
break;
|
|
case 2: // bind
|
|
WARN_LOG(NAOMI, "netdimm: bind not implemented");
|
|
returnToNaomi(true, buffer[1], -1);
|
|
break;
|
|
case 3: // close
|
|
{
|
|
const int sockidx = buffer[1];
|
|
const sock_t sockfd = getSocket(sockidx);
|
|
int rc;
|
|
if (sockfd == INVALID_SOCKET)
|
|
{
|
|
WARN_LOG(NAOMI, "closesocket(%d) invalid socket", sockidx);
|
|
rc = -1;
|
|
}
|
|
else
|
|
{
|
|
rc = sockets[sockidx - 1].close();
|
|
INFO_LOG(NAOMI, "closesocket(%d) %d -> %d", sockidx, sockfd, rc);
|
|
}
|
|
returnToNaomi(rc != 0, sockidx, rc);
|
|
break;
|
|
}
|
|
case 4: // connect
|
|
{
|
|
const int sockidx = buffer[1];
|
|
const sockaddr_in *addr = (const sockaddr_in *)&dimm_data[buffer[2]];
|
|
sock_t sockfd = getSocket(sockidx);
|
|
int rc;
|
|
if (sockfd == INVALID_SOCKET)
|
|
{
|
|
WARN_LOG(NAOMI, "connect(%d, %x) invalid socket", sockidx, htonl(addr->sin_addr.s_addr));
|
|
rc = -1;
|
|
}
|
|
else
|
|
{
|
|
//socklen_t len = (socklen_t)buffer[3];
|
|
sockaddr_in a {};
|
|
a.sin_family = AF_INET; // for some reason the family is in network order too. Just ignore it.
|
|
a.sin_port = addr->sin_port;
|
|
a.sin_addr.s_addr = addr->sin_addr.s_addr;
|
|
rc = connect(sockfd, (sockaddr *)&a, sizeof(a));
|
|
if (rc == -1)
|
|
{
|
|
int error = get_last_error();
|
|
if (error == L_EINPROGRESS || error == L_EWOULDBLOCK)
|
|
{
|
|
sockets[sockidx - 1].connecting = true;
|
|
sockets[sockidx - 1].connectTime = sh4_sched_now64();
|
|
sh4_sched_request(schedId, POLL_CYCLES);
|
|
INFO_LOG(NAOMI, "connect(%d, %x:%d) delayed", sockidx, htonl(a.sin_addr.s_addr), htons(a.sin_port));
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
sockets[sockidx - 1].lastError = error;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (finalTuned)
|
|
set_non_blocking(sockfd);
|
|
}
|
|
INFO_LOG(NAOMI, "connect(%d, %x:%d) -> %d", sockidx, htonl(a.sin_addr.s_addr), htons(a.sin_port), rc);
|
|
}
|
|
returnToNaomi(rc != 0, sockidx, rc);
|
|
break;
|
|
}
|
|
case 5: // getIpByDns
|
|
{
|
|
char *name = (char *)&dimm_data[buffer[1]];
|
|
INFO_LOG(NAOMI, "getIpByDns %s", name);
|
|
//dnsInProgress = true;
|
|
//sh4_sched_request(schedId, POLL_CYCLES * 10);
|
|
|
|
//int len = buffer[2];
|
|
//u32 dns1 = buffer[3];
|
|
//u32 dns2 = buffer[4];
|
|
returnToNaomi(false, 0, serverIp);
|
|
break;
|
|
}
|
|
case 6: // inet_addr
|
|
WARN_LOG(NAOMI, "netdimm: inet_addr not implemented");
|
|
returnToNaomi(true, 0, -1);
|
|
break;
|
|
case 7: // ioctl
|
|
WARN_LOG(NAOMI, "netdimm: ioctl not implemented");
|
|
returnToNaomi(true, buffer[1], -1);
|
|
break;
|
|
case 8: // listen
|
|
WARN_LOG(NAOMI, "netdimm: listen not implemented");
|
|
returnToNaomi(true, buffer[1], -1);
|
|
break;
|
|
case 9: // recv
|
|
{
|
|
const int sockidx = buffer[1];
|
|
sock_t sockfd = getSocket(sockidx);
|
|
int rc;
|
|
if (sockfd == INVALID_SOCKET)
|
|
{
|
|
WARN_LOG(NAOMI, "recv(%d) invalid socket", sockidx);
|
|
rc = -1;
|
|
}
|
|
else
|
|
{
|
|
u32 len = buffer[3];
|
|
u32 offset = buffer[2] & (dimm_data_size - 1);
|
|
u8 *data = &dimm_data[offset];
|
|
rc = recv(sockfd, (char *)data, len, 0);
|
|
if (rc == -1)
|
|
{
|
|
int error = get_last_error();
|
|
if (error == L_EAGAIN || error == L_EWOULDBLOCK)
|
|
{
|
|
sockets[sockidx - 1].receiving = true;
|
|
sockets[sockidx - 1].recvTime = sh4_sched_now64();
|
|
sockets[sockidx - 1].recvData = data;
|
|
sockets[sockidx - 1].recvLen = len;
|
|
sh4_sched_request(schedId, POLL_CYCLES);
|
|
INFO_LOG(NAOMI, "recv(%d, %d) delayed", sockidx, len);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
sockets[sockidx - 1].lastError = get_last_error();
|
|
}
|
|
}
|
|
#ifdef HTTP_TRACE
|
|
else if (rc > 0)
|
|
{
|
|
fwrite(data, 1, rc, stdout);
|
|
fflush(stdout);
|
|
}
|
|
#endif
|
|
INFO_LOG(NAOMI, "recv(%d, %d) -> %d", sockidx, len, rc);
|
|
}
|
|
returnToNaomi(rc == -1, sockidx, rc);
|
|
break;
|
|
}
|
|
case 10: // send
|
|
{
|
|
const int sockidx = buffer[1];
|
|
sock_t sockfd = getSocket(sockidx);
|
|
int rc;
|
|
if (sockfd == INVALID_SOCKET)
|
|
{
|
|
INFO_LOG(NAOMI, "send(%d) invalid socket", sockidx);
|
|
rc = -1;
|
|
}
|
|
else
|
|
{
|
|
u32 len = buffer[3];
|
|
u32 offset = buffer[2] & (dimm_data_size - 1);
|
|
u8 *data = &dimm_data[offset];
|
|
rc = send(sockfd, (const char *)data, len, 0);
|
|
if (rc == -1)
|
|
{
|
|
int error = get_last_error();
|
|
if (error == L_EAGAIN || error == L_EWOULDBLOCK)
|
|
{
|
|
sockets[sockidx - 1].sending = true;
|
|
sockets[sockidx - 1].sendTime = sh4_sched_now64();
|
|
sockets[sockidx - 1].sendData = data;
|
|
sockets[sockidx - 1].sendLen = len;
|
|
sh4_sched_request(schedId, POLL_CYCLES);
|
|
INFO_LOG(NAOMI, "send(%d, %d) delayed", sockidx, len);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
sockets[sockidx - 1].lastError = get_last_error();
|
|
}
|
|
}
|
|
INFO_LOG(NAOMI, "send(%d, %d) -> %d", sockidx, len, rc);
|
|
#ifdef HTTP_TRACE
|
|
fwrite(data, 1, len, stdout);
|
|
fflush(stdout);
|
|
#endif
|
|
}
|
|
returnToNaomi(rc == -1, sockidx, rc);
|
|
break;
|
|
}
|
|
case 11: // openSocket
|
|
{
|
|
const u32 domain = buffer[1];
|
|
const u32 type = buffer[2];
|
|
const u32 protocol = buffer[3];
|
|
const sock_t fd = socket(domain, type, protocol);
|
|
int sockidx = -1;
|
|
if (fd != INVALID_SOCKET)
|
|
{
|
|
// FIXME async mode still not right with FT
|
|
if (!finalTuned)
|
|
set_non_blocking(fd);
|
|
size_t i = 0;
|
|
for (; i < sockets.size(); i++)
|
|
if (sockets[i].fd == INVALID_SOCKET)
|
|
break;
|
|
if (i == sockets.size())
|
|
sockets.emplace_back(fd);
|
|
else
|
|
sockets[i].fd = fd;
|
|
sockidx = i + 1;
|
|
}
|
|
INFO_LOG(NAOMI, "openSocket(%d, %d, %d) %d -> %d", domain, type, protocol, fd, sockidx);
|
|
returnToNaomi(sockidx == -1, 0, sockidx);
|
|
break;
|
|
}
|
|
case 12: // netSelect
|
|
{
|
|
const u32 readFds = buffer[2];
|
|
const u32 writeFds = buffer[3];
|
|
const u32 exceptFds = buffer[4];
|
|
const u32 timeoutAddr = buffer[4];
|
|
int nfds = -1;
|
|
fd_set read {};
|
|
fd_set write {};
|
|
fd_set except {};
|
|
timeval timeout;
|
|
|
|
const auto& setFdsets = [this, buffer, &nfds](u32 fdsOffset, fd_set *fdset)
|
|
{
|
|
if (fdsOffset == 0)
|
|
return;
|
|
fd_set fds;
|
|
memcpy(&fds, &dimm_data[fdsOffset & (dimm_data_size - 1)], std::min<size_t>(sizeof(fds), 32));
|
|
const int count = buffer[1];
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
if (FD_ISSET(i, &fds))
|
|
{
|
|
sock_t sockfd = getSocket(i);
|
|
if (sockfd != INVALID_SOCKET) {
|
|
FD_SET(sockfd, fdset);
|
|
nfds = std::max(nfds, (int)sockfd);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
setFdsets(readFds, &read);
|
|
setFdsets(writeFds, &write);
|
|
setFdsets(exceptFds, &except);
|
|
|
|
if (timeoutAddr != 0)
|
|
{
|
|
timeout.tv_sec = *(u32 *)&dimm_data[timeoutAddr & (dimm_data_size - 1)];
|
|
timeout.tv_usec = *(u32 *)&dimm_data[(timeoutAddr + 4) & (dimm_data_size - 1)];
|
|
}
|
|
int rc = select(nfds + 1, &read, &write, &except, timeoutAddr == 0 ? nullptr : &timeout);
|
|
INFO_LOG(NAOMI, "select(%d, %x, %x, %x, %x) -> %d", nfds, readFds, writeFds, exceptFds, timeoutAddr, rc);
|
|
returnToNaomi(rc == -1, 0, rc);
|
|
break;
|
|
}
|
|
case 13: // shutdown (not implemented on real hardware)
|
|
WARN_LOG(NAOMI, "netdimm: shutdown not implemented");
|
|
returnToNaomi(true, buffer[1], -3);
|
|
break;
|
|
case 14: // setsockopt
|
|
WARN_LOG(NAOMI, "netdimm: setsockopt not implemented");
|
|
returnToNaomi(true, buffer[1], -1);
|
|
break;
|
|
case 15: // getsockopt
|
|
WARN_LOG(NAOMI, "netdimm: getsockopt not implemented");
|
|
returnToNaomi(true, buffer[1], -1);
|
|
break;
|
|
case 16: // settimeout
|
|
{
|
|
const int sockidx = buffer[1];
|
|
sock_t sockfd = getSocket(sockidx);
|
|
int rc;
|
|
if (sockfd == INVALID_SOCKET)
|
|
{
|
|
WARN_LOG(NAOMI, "settimeout(%d) invalid socket", sockidx);
|
|
rc = -1;
|
|
}
|
|
else
|
|
{
|
|
sockets[sockidx - 1].connectTimeout = (u64)buffer[2] * SH4_MAIN_CLOCK / 1000;
|
|
sockets[sockidx - 1].sendTimeout = (u64)buffer[3] * SH4_MAIN_CLOCK / 1000;
|
|
sockets[sockidx - 1].recvTimeout = (u64)buffer[4] * SH4_MAIN_CLOCK / 1000;
|
|
INFO_LOG(NAOMI, "setTimeout(%d, %d, %d, %d)", sockidx, buffer[2], buffer[3], buffer[4]);
|
|
rc = 0;
|
|
}
|
|
returnToNaomi(rc != 0, sockidx, 0);
|
|
break;
|
|
}
|
|
case 17: // geterrno
|
|
{
|
|
int sockidx = buffer[1];
|
|
sock_t sockfd = getSocket(sockidx);
|
|
if (sockfd != INVALID_SOCKET)
|
|
{
|
|
int rc = sockets[sockidx - 1].lastError;
|
|
INFO_LOG(NAOMI, "geterrno(%d) -> %d", sockidx, rc);
|
|
returnToNaomi(false, sockidx, rc);
|
|
}
|
|
else
|
|
{
|
|
returnToNaomi(true, sockidx, -1);
|
|
}
|
|
break;
|
|
}
|
|
case 18: // routeAdd
|
|
WARN_LOG(NAOMI, "netdimm: routeAdd not implemented");
|
|
returnToNaomi(true, 0, -1);
|
|
break;
|
|
case 19: // routeDelete
|
|
WARN_LOG(NAOMI, "netdimm: routeDelete not implemented");
|
|
returnToNaomi(true, 0, -1);
|
|
break;
|
|
|
|
case 20: // getParambyDHCP
|
|
WARN_LOG(NAOMI, "netdimm: getParambyDHCP not implemented");
|
|
returnToNaomi(true, 0, -1);
|
|
break;
|
|
case 21: // modifyMyIPaddr
|
|
WARN_LOG(NAOMI, "netdimm: modifyMyIPaddr not implemented");
|
|
returnToNaomi(true, 0, -1);
|
|
break;
|
|
case 22: // recvfrom
|
|
WARN_LOG(NAOMI, "netdimm: recvfrom not implemented");
|
|
returnToNaomi(true, buffer[1], -1);
|
|
break;
|
|
case 23: // sendto
|
|
WARN_LOG(NAOMI, "netdimm: sendto not implemented");
|
|
returnToNaomi(true, buffer[1], -1);
|
|
break;
|
|
|
|
default:
|
|
WARN_LOG(NAOMI, "netdimm: Invalid Net command: %d", cmd);
|
|
returnToNaomi(true, 0, 0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void NetDimm::process()
|
|
{
|
|
INFO_LOG(NAOMI, "NetDIMM cmd %04x sock %d offset %04x paramh/l %04x %04x", (dimm_command >> 9) & 0x3f,
|
|
dimm_command & 0xff, dimm_offsetl, dimm_parameterh, dimm_parameterl);
|
|
|
|
int cmdGroup = (dimm_command >> 13) & 3;
|
|
int cmd = (dimm_command >> 9) & 0xf;
|
|
switch (cmdGroup)
|
|
{
|
|
case 0: // system commands
|
|
systemCmd(cmd);
|
|
break;
|
|
case 1: // network client
|
|
netCmd(cmd);
|
|
break;
|
|
default:
|
|
WARN_LOG(NAOMI, "Unknown DIMM command group %d cmd %x", cmdGroup, cmd);
|
|
returnToNaomi(true, 0, -1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void NetDimm::Deserialize(Deserializer &deser)
|
|
{
|
|
GDCartridge::Deserialize(deser);
|
|
for (Socket& socket : sockets)
|
|
socket.close();
|
|
if (deser.version() >= Deserializer::V36 && deser.version() < Deserializer::V53)
|
|
{
|
|
// moved to parent class in v53
|
|
deser >> dimm_command;
|
|
deser >> dimm_offsetl;
|
|
deser >> dimm_parameterl;
|
|
deser >> dimm_parameterh;
|
|
sh4_sched_deserialize(deser, schedId);
|
|
}
|
|
}
|