diff --git a/core/deps/picotcp/include/pico_socket.h b/core/deps/picotcp/include/pico_socket.h index 9162d6cd1..d870368f6 100644 --- a/core/deps/picotcp/include/pico_socket.h +++ b/core/deps/picotcp/include/pico_socket.h @@ -11,7 +11,7 @@ #include "pico_protocol.h" #include "pico_tree.h" -#ifdef __linux__ +#if 0 // __linux__ #define PICO_DEFAULT_SOCKETQ (16 * 1024) /* Linux host, so we want full throttle */ #else #define PICO_DEFAULT_SOCKETQ (6 * 1024) /* seems like an acceptable default for small embedded systems */ diff --git a/core/deps/picotcp/modules/pico_tcp.c b/core/deps/picotcp/modules/pico_tcp.c index 0384c8f5b..2564592f2 100644 --- a/core/deps/picotcp/modules/pico_tcp.c +++ b/core/deps/picotcp/modules/pico_tcp.c @@ -24,7 +24,7 @@ #define TCP_TIME (pico_time)(PICO_TIME_MS()) -#define PICO_TCP_RTO_MIN (70) +#define PICO_TCP_RTO_MIN (1000) #define PICO_TCP_RTO_MAX (120000) #define PICO_TCP_IW 2 #define PICO_TCP_SYN_TO 2000u @@ -1444,6 +1444,7 @@ static inline void tcp_fill_rst_header(struct pico_frame *fr, struct pico_tcp_hd if(!(hdr1->flags & PICO_TCP_ACK)) hdr->ack = long_be(long_be(((struct pico_tcp_hdr *)(fr->transport_hdr))->seq) + fr->payload_len); + hdr->crc = 0; hdr->crc = short_be(pico_tcp_checksum(f)); } @@ -1477,6 +1478,7 @@ int pico_tcp_reply_rst(struct pico_frame *fr) if (0) { #ifdef PICO_SUPPORT_IPV4 } else if (IS_IPV4(f)) { + f->local_ip.addr = fr->sock->local_addr.ip4.addr; // Masqueraded tcp_dbg("Pushing IPv4 reset frame...\n"); pico_ipv4_frame_push(f, &(((struct pico_ipv4_hdr *)(f->net_hdr))->dst), PICO_PROTO_TCP); #endif @@ -1866,7 +1868,7 @@ static int tcp_rto_xmit(struct pico_socket_tcp *t, struct pico_frame *f) t->snd_last_out = SEQN(cpy); add_retransmission_timer(t, (t->rto << (++t->backoff)) + TCP_TIME); tcp_dbg("TCP_CWND, %lu, %u, %u, %u\n", TCP_TIME, t->cwnd, t->ssthresh, t->in_flight); - tcp_dbg("Sending RTO!\n"); + tcp_dbg("Sending RTO! port %d\n", short_be(t->sock.remote_port)); return 1; } else { tcp_dbg("RTO fail, retry!\n"); @@ -1939,7 +1941,7 @@ static void tcp_retrans_timeout(pico_time val, void *sock) return; } - tcp_dbg("TIMEOUT! backoff = %d, rto: %d\n", t->backoff, t->rto); + tcp_dbg("TIMEOUT! backoff = %d, rto: %d, port: %d\n", t->backoff, t->rto, short_be(t->sock.remote_port)); t->retrans_tmr_due = 0ull; if (tcp_is_allowed_to_send(t)) { @@ -1986,11 +1988,17 @@ static void add_retransmission_timer(struct pico_socket_tcp *t, pico_time next_t val = next_ts + (t->rto << t->backoff); } } + if (next_ts == 0) + { + t->retrans_tmr_due = 0; + return; + } + tcp_dbg("add_retransmission_timer: timestamp %d, timeout %d, rto %d\n", (uint32_t)(next_ts - now), (uint32_t)(val - now), t->rto << t->backoff); } else { val = next_ts; } - if ((val > 0) || (val > now)) { + if (val > now) { t->retrans_tmr_due = val; } else { t->retrans_tmr_due = now + 1; @@ -2837,10 +2845,10 @@ int pico_tcp_input(struct pico_socket *s, struct pico_frame *f) f->payload = (f->transport_hdr + ((hdr->len & 0xf0u) >> 2u)); f->payload_len = (uint16_t)(f->transport_len - ((hdr->len & 0xf0u) >> 2u)); - tcp_dbg("[sam] TCP> [tcp input] t_len: %u\n", f->transport_len); - tcp_dbg("[sam] TCP> flags = 0x%02x\n", hdr->flags); - tcp_dbg("[sam] TCP> s->state >> 8 = %u\n", s->state >> 8); - tcp_dbg("[sam] TCP> [tcp input] socket: %p state: %d <-- local port:%u remote port: %u seq: 0x%08x ack: 0x%08x flags: 0x%02x t_len: %u, hdr: %u payload: %d\n", s, s->state >> 8, short_be(hdr->trans.dport), short_be(hdr->trans.sport), SEQN(f), ACKN(f), hdr->flags, f->transport_len, (hdr->len & 0xf0) >> 2, f->payload_len ); +// tcp_dbg("[sam] TCP> [tcp input] t_len: %u\n", f->transport_len); +// tcp_dbg("[sam] TCP> flags = 0x%02x\n", hdr->flags); +// tcp_dbg("[sam] TCP> s->state >> 8 = %u\n", s->state >> 8); + tcp_dbg("[tcp input] socket: %p state: %d <-- local port:%u remote port: %u seq: 0x%08x ack: 0x%08x flags: 0x%02x t_len: %u, hdr: %u payload: %d\n", s, s->state >> 8, short_be(hdr->trans.dport), short_be(hdr->trans.sport), SEQN(f), ACKN(f), hdr->flags, f->transport_len, (hdr->len & 0xf0) >> 2, f->payload_len ); /* This copy of the frame has the current socket as owner */ f->sock = s; @@ -2960,11 +2968,11 @@ int pico_tcp_output(struct pico_socket *s, int loop_score) if (t->x_mode != PICO_TCP_WINDOW_FULL) { tcp_dbg("TCP> RIGHT SIZING (rwnd: %d, frame len: %d\n", t->recv_wnd << t->recv_wnd_scale, f->payload_len); tcp_dbg("In window full...\n"); - t->snd_nxt = SEQN(una); - t->snd_retry = SEQN(una); + t->snd_nxt = SEQN(f); + t->snd_retry = SEQN(f); t->x_mode = PICO_TCP_WINDOW_FULL; } - + // TODO ? add_retransmission_timer(t, 0); break; } diff --git a/core/hw/modem/modem.cpp b/core/hw/modem/modem.cpp index 01f3f6ad9..d5ce9caf1 100644 --- a/core/hw/modem/modem.cpp +++ b/core/hw/modem/modem.cpp @@ -284,7 +284,11 @@ static int modem_sched_func(int tag, int cycles, int jitter) if (connected_time == 0) connected_time = sh4_sched_now64(); - callback_cycles = SH4_MAIN_CLOCK / 1000000 * 238; // 238 us + // This value is critical. Setting it too low will cause some sockets to stall. + // Check Sonic Adventure 2 and Samba de Amigo (PAL) integrated browsers. + // 143 us/bytes corresponds to 56K but is too low for SA2 and SdA. They need >= 160. + // Using 166 for now (~ 48Kbps) + callback_cycles = SH4_MAIN_CLOCK / 1000000 * 166; modem_regs.reg1e.TDBE = 1; if (!modem_regs.reg1e.RDBF) @@ -302,9 +306,6 @@ static int modem_sched_func(int tag, int cycles, int jitter) if (modem_regs.reg04.FIFOEN) SET_STATUS_BIT(0x0c, modem_regs.reg0c.RXFNE, 1); SET_STATUS_BIT(0x01, modem_regs.reg01.RXHF, 1); - - // Set small value to receive following data quickly. - callback_cycles = SH4_MAIN_CLOCK / 1000000 * 62; // 62 us } } diff --git a/core/hw/modem/picoppp.cpp b/core/hw/modem/picoppp.cpp index 3981a2436..c6a9e4272 100644 --- a/core/hw/modem/picoppp.cpp +++ b/core/hw/modem/picoppp.cpp @@ -65,10 +65,93 @@ static struct pico_socket *pico_tcp_socket, *pico_udp_socket; struct pico_ip4 public_ip; struct pico_ip4 afo_ip; -// src socket -> socket fd -static std::map tcp_sockets; +struct socket_pair +{ + socket_pair() : pico_sock(nullptr), native_sock(INVALID_SOCKET) {} + socket_pair(pico_socket *pico_sock, sock_t native_sock) : pico_sock(pico_sock), native_sock(native_sock) {} + ~socket_pair() { + if (pico_sock != nullptr) + pico_socket_close(pico_sock); + if (native_sock != INVALID_SOCKET) + closesocket(native_sock); + } + socket_pair(socket_pair &&) = default; + socket_pair(const socket_pair&) = delete; + socket_pair& operator=(const socket_pair&) = delete; + + pico_socket *pico_sock; + sock_t native_sock; + std::vector in_buffer; + + void receive_native() + { + size_t len; + const u8 *data; + u8 buf[512]; + + if (!in_buffer.empty()) + { + len = in_buffer.size(); + data = &in_buffer[0]; + } + else + { + if (native_sock == INVALID_SOCKET) + return; + int r = recv(native_sock, buf, sizeof(buf), 0); + if (r == 0) + { + INFO_LOG(MODEM, "Socket[%d] recv(%zd) returned 0 -> EOF", short_be(pico_sock->remote_port), sizeof(buf)); + pico_socket_shutdown(pico_sock, PICO_SHUT_RDWR); + closesocket(native_sock); + native_sock = INVALID_SOCKET; + return; + } + if (r < 0) + { + if (get_last_error() != L_EAGAIN && get_last_error() != L_EWOULDBLOCK) + { + perror("recv tcp socket"); + closesocket(native_sock); + native_sock = INVALID_SOCKET; + pico_socket_shutdown(pico_sock, PICO_SHUT_RDWR); + } + return; + } + len = r; + data = buf; + } + if (pico_sock->remote_port == short_be(5011) && len >= 5) + { + // Visual Concepts sport games + if (buf[0] == 1) + memcpy(&buf[1], &pico_sock->local_addr.ip4.addr, 4); + } + + int r2 = pico_socket_send(pico_sock, data, len); + if (r2 < 0) + INFO_LOG(MODEM, "error TCP sending: %s", strerror(pico_err)); + else if (r2 < (int)len) + { + if (r2 > 0 || in_buffer.empty()) + { + len -= r2; + std::vector remain(len); + memcpy(&remain[0], &data[r2], len); + std::swap(in_buffer, remain); + } + } + else + { + in_buffer.clear(); + } + } +}; + +// tcp sockets +static std::map tcp_sockets; static std::map tcp_connecting_sockets; -// src port -> socket fd +// udp sockets: src port -> socket fd static std::map udp_sockets; static const uint16_t games_udp_ports[] = { @@ -105,6 +188,9 @@ static const uint16_t games_tcp_ports[] = { // listening port -> socket fd static std::map tcp_listening_sockets; +static bool pico_stack_inited; +static bool pico_thread_running = false; + static void read_native_sockets(); void get_host_by_name(const char *name, struct pico_ip4 dnsaddr); int get_dns_answer(struct pico_ip4 *address, struct pico_ip4 dnsaddr); @@ -131,10 +217,17 @@ static int modem_write(struct pico_device *dev, const void *data, int len) u8 *p = (u8 *)data; in_buffer_lock.lock(); - while (len > 0) + for (int i = 0; i < len; i++) { + while (in_buffer.size() > 1024) + { + in_buffer_lock.unlock(); + if (!pico_thread_running) + return 0; + usleep(5000); + in_buffer_lock.lock(); + } in_buffer.push(*p++); - len--; } in_buffer_lock.unlock(); @@ -175,8 +268,6 @@ static void read_from_dc_socket(pico_socket *pico_sock, sock_t nat_sock) if (send(nat_sock, buf, r, 0) < r) { perror("tcp_callback send"); - closesocket(nat_sock); - pico_socket_close(pico_sock); tcp_sockets.erase(pico_sock); } } @@ -194,7 +285,7 @@ static void tcp_callback(uint16_t ev, struct pico_socket *s) } else { - read_from_dc_socket(it->first, it->second); + read_from_dc_socket(it->first, it->second.native_sock); } } @@ -215,7 +306,7 @@ static void tcp_callback(uint16_t ev, struct pico_socket *s) else { pico_ipv4_to_string(peer, sock_a->local_addr.ip4.addr); - //printf("Connection established from %s:%d to %08x:%d\n", peer, short_be(port), sock_a->local_addr.ip4.addr, short_be(sock_a->local_port)); + //printf("Connection established from port %d to %s:%d\n", short_be(port), peer, short_be(sock_a->local_port)); pico_socket_setoption(sock_a, PICO_TCP_NODELAY, &yes); /* Set keepalive options */ // uint32_t ka_val = 5; @@ -259,7 +350,9 @@ static void tcp_callback(uint16_t ev, struct pico_socket *s) { set_tcp_nodelay(sockfd); - tcp_sockets[sock_a] = sockfd; + tcp_sockets.emplace(std::piecewise_construct, + std::forward_as_tuple(sock_a), + std::forward_as_tuple(sock_a, sockfd)); } } } @@ -269,7 +362,6 @@ static void tcp_callback(uint16_t ev, struct pico_socket *s) auto it = tcp_sockets.find(s); if (it != tcp_sockets.end()) { - closesocket(it->second); tcp_sockets.erase(it); } else @@ -289,14 +381,9 @@ static void tcp_callback(uint16_t ev, struct pico_socket *s) INFO_LOG(MODEM, "Socket error received: %s", strerror(pico_err)); auto it = tcp_sockets.find(s); if (it == tcp_sockets.end()) - { INFO_LOG(MODEM, "PICO_SOCK_EV_ERR: Unknown socket: remote port %d", short_be(s->remote_port)); - } else - { - closesocket(it->second); tcp_sockets.erase(it); - } } if (ev & PICO_SOCK_EV_CLOSE) @@ -308,7 +395,8 @@ static void tcp_callback(uint16_t ev, struct pico_socket *s) } else { - shutdown(it->second, SHUT_WR); + if (it->second.native_sock != INVALID_SOCKET) + shutdown(it->second.native_sock, SHUT_WR); pico_socket_shutdown(s, PICO_SHUT_RD); } } @@ -420,7 +508,11 @@ static void read_native_sockets() } set_non_blocking(sockfd); set_tcp_nodelay(sockfd); - tcp_sockets[ps] = sockfd; + + tcp_sockets.emplace(std::piecewise_construct, + std::forward_as_tuple(ps), + std::forward_as_tuple(ps, sockfd)); + } // Check connecting outbound TCP sockets @@ -461,7 +553,10 @@ static void read_native_sockets() else { set_tcp_nodelay(it->second); - tcp_sockets[it->first] = it->second; + + tcp_sockets.emplace(std::piecewise_construct, + std::forward_as_tuple(it->first), + std::forward_as_tuple(it->first, it->second)); read_from_dc_socket(it->first, it->second); } @@ -469,16 +564,9 @@ static void read_native_sockets() } } - char buf[1500]; // FIXME MTU ? + char buf[1500]; struct pico_msginfo msginfo; - // If modem buffer is full, wait - in_buffer_lock.lock(); - size_t in_buffer_size = in_buffer.size(); - in_buffer_lock.unlock(); - if (in_buffer_size >= 256) - return; - // Read UDP sockets for (auto it = udp_sockets.begin(); it != udp_sockets.end(); it++) { @@ -510,45 +598,11 @@ static void read_native_sockets() // Read TCP sockets for (auto it = tcp_sockets.begin(); it != tcp_sockets.end(); ) { - uint32_t space; - pico_tcp_get_bufspace_out(it->first, &space); - if (space < sizeof(buf)) - { - // Wait for the out buffer to empty a bit - it++; - continue; - } - r = recv(it->second, buf, sizeof(buf), 0); - if (r > 0) - { - if (it->first->remote_port == short_be(5011) && r >= 5) - { - // Visual Concepts sport games - if (buf[0] == 1) - memcpy(&buf[1], &it->first->local_addr.ip4.addr, 4); - } - - int r2 = pico_socket_send(it->first, buf, r); - if (r2 < 0) - INFO_LOG(MODEM, "error TCP sending: %s", strerror(pico_err)); - else if (r2 < r) - // FIXME EAGAIN errors. Need to buffer data or wait for call back. - INFO_LOG(MODEM, "truncated send: %d -> %d", r, r2); - } - else if (r == 0) - { - pico_socket_shutdown(it->first, PICO_SHUT_WR); - shutdown(it->second, SHUT_RD); - } - else if (r < 0 && get_last_error() != L_EAGAIN && get_last_error() != L_EWOULDBLOCK) - { - perror("recv tcp socket"); - closesocket(it->second); - pico_socket_close(it->first); + it->second.receive_native(); + if (it->second.pico_sock == nullptr) it = tcp_sockets.erase(it); - continue; - } - it++; + else + it++; } } @@ -557,11 +611,6 @@ void close_native_sockets() for (auto it = udp_sockets.begin(); it != udp_sockets.end(); it++) closesocket(it->second); udp_sockets.clear(); - for (auto it = tcp_sockets.begin(); it != tcp_sockets.end(); it++) - { - pico_socket_close(it->first); - closesocket(it->second); - } tcp_sockets.clear(); for (auto it = tcp_connecting_sockets.begin(); it != tcp_connecting_sockets.end(); it++) { @@ -576,21 +625,6 @@ static int modem_set_speed(struct pico_device *dev, uint32_t speed) return 0; } -#if 0 // _WIN32 -static void usleep(unsigned int usec) -{ - HANDLE timer; - LARGE_INTEGER ft; - - ft.QuadPart = -(10 * (__int64)usec); - - timer = CreateWaitableTimer(NULL, TRUE, NULL); - SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0); - WaitForSingleObject(timer, INFINITE); - CloseHandle(timer); -} -#endif - static void check_dns_entries() { static uint32_t dns_query_start = 0; @@ -669,9 +703,6 @@ static void check_dns_entries() } } -static bool pico_stack_inited; -static bool pico_thread_running = false; - static void *pico_thread_func(void *) { struct pico_ip4 ipaddr; @@ -769,6 +800,18 @@ static void *pico_thread_func(void *) tcp_listening_sockets[port] = sockfd; } + { + std::queue empty; + in_buffer_lock.lock(); + std::swap(in_buffer, empty); + in_buffer_lock.unlock(); + + std::queue empty2; + out_buffer_lock.lock(); + std::swap(out_buffer, empty2); + out_buffer_lock.unlock(); + } + pico_ppp_set_serial_read(ppp, modem_read); pico_ppp_set_serial_write(ppp, modem_write); pico_ppp_set_serial_set_speed(ppp, modem_set_speed); @@ -780,7 +823,7 @@ static void *pico_thread_func(void *) read_native_sockets(); pico_stack_tick(); check_dns_entries(); - usleep(1000); + usleep(5000); } for (auto it = tcp_listening_sockets.begin(); it != tcp_listening_sockets.end(); it++)