| // Copyright lowRISC contributors. | 
 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
 | // SPDX-License-Identifier: Apache-2.0 | 
 |  | 
 | #include "tcp_server.h" | 
 |  | 
 | #include <assert.h> | 
 | #include <errno.h> | 
 | #include <fcntl.h> | 
 | #include <netinet/in.h> | 
 | #include <netinet/tcp.h> | 
 | #include <pthread.h> | 
 | #include <stdio.h> | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 | #include <sys/socket.h> | 
 | #include <sys/time.h> | 
 | #include <sys/types.h> | 
 | #include <unistd.h> | 
 |  | 
 | /** | 
 |  * Simple buffer for passing data between TCP sockets and DPI modules | 
 |  */ | 
 | const int BUFSIZE_BYTE = 256; | 
 |  | 
 | struct tcp_buf { | 
 |   unsigned int rptr; | 
 |   unsigned int wptr; | 
 |   char buf[BUFSIZE_BYTE]; | 
 | }; | 
 |  | 
 | /** | 
 |  * TCP Server thread context structure | 
 |  */ | 
 | struct tcp_server_ctx { | 
 |   // Writeable by the host thread | 
 |   char *display_name; | 
 |   uint16_t listen_port; | 
 |   volatile bool socket_run; | 
 |   // Writeable by the server thread | 
 |   tcp_buf *buf_in; | 
 |   tcp_buf *buf_out; | 
 |   int sfd;  // socket fd | 
 |   int cfd;  // client fd | 
 |   pthread_t sock_thread; | 
 | }; | 
 |  | 
 | static bool tcp_buffer_is_full(struct tcp_buf *buf) { | 
 |   if (buf->wptr >= buf->rptr) { | 
 |     return (buf->wptr - buf->rptr) == (BUFSIZE_BYTE - 1); | 
 |   } else { | 
 |     return (buf->rptr - buf->wptr) == 1; | 
 |   } | 
 | } | 
 |  | 
 | static bool tcp_buffer_is_empty(struct tcp_buf *buf) { | 
 |   return (buf->wptr == buf->rptr); | 
 | } | 
 |  | 
 | static void tcp_buffer_put_byte(struct tcp_buf *buf, char dat) { | 
 |   bool done = false; | 
 |   while (!done) { | 
 |     if (!tcp_buffer_is_full(buf)) { | 
 |       buf->buf[buf->wptr++] = dat; | 
 |       buf->wptr %= BUFSIZE_BYTE; | 
 |       done = true; | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | static bool tcp_buffer_get_byte(struct tcp_buf *buf, char *dat) { | 
 |   if (tcp_buffer_is_empty(buf)) { | 
 |     return false; | 
 |   } | 
 |   *dat = buf->buf[buf->rptr++]; | 
 |   buf->rptr %= BUFSIZE_BYTE; | 
 |   return true; | 
 | } | 
 |  | 
 | static struct tcp_buf *tcp_buffer_new(void) { | 
 |   struct tcp_buf *buf_new; | 
 |   buf_new = (struct tcp_buf *)malloc(sizeof(struct tcp_buf)); | 
 |   buf_new->rptr = 0; | 
 |   buf_new->wptr = 0; | 
 |   return buf_new; | 
 | } | 
 |  | 
 | static void tcp_buffer_free(struct tcp_buf **buf) { | 
 |   free(*buf); | 
 |   *buf = NULL; | 
 | } | 
 |  | 
 | /** | 
 |  * Start a TCP server | 
 |  * | 
 |  * This function creates attempts to create a new TCP socket instance. The | 
 |  * socket is a non-blocking stream socket, with buffering disabled. | 
 |  * | 
 |  * @param ctx context object | 
 |  * @return 0 on success, -1 in case of an error | 
 |  */ | 
 | static int start(struct tcp_server_ctx *ctx) { | 
 |   int rv; | 
 |  | 
 |   assert(ctx->sfd == 0 && "Server already started."); | 
 |  | 
 |   // create socket | 
 |   int sfd = socket(AF_INET, SOCK_STREAM, 0); | 
 |   if (sfd == -1) { | 
 |     fprintf(stderr, "%s: Unable to create socket: %s (%d)\n", ctx->display_name, | 
 |             strerror(errno), errno); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   rv = fcntl(sfd, F_SETFL, O_NONBLOCK); | 
 |   if (rv != 0) { | 
 |     fprintf(stderr, "%s: Unable to make socket non-blocking: %s (%d)\n", | 
 |             ctx->display_name, strerror(errno), errno); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   // reuse existing socket (if existing) | 
 |   int reuse_socket = 1; | 
 |   rv = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse_socket, sizeof(int)); | 
 |   if (rv != 0) { | 
 |     fprintf(stderr, "%s: Unable to set socket options: %s (%d)\n", | 
 |             ctx->display_name, strerror(errno), errno); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   // stop tcp socket from buffering (buffering prevents timely responses to | 
 |   // OpenOCD which severly limits debugging performance) | 
 |   int tcp_nodelay = 1; | 
 |   rv = setsockopt(sfd, IPPROTO_TCP, TCP_NODELAY, &tcp_nodelay, sizeof(int)); | 
 |   if (rv != 0) { | 
 |     fprintf(stderr, "%s: Unable to set socket nodelay: %s (%d)\n", | 
 |             ctx->display_name, strerror(errno), errno); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   // bind server | 
 |   struct sockaddr_in addr; | 
 |   memset(&addr, 0, sizeof(addr)); | 
 |   addr.sin_family = AF_INET; | 
 |   addr.sin_addr.s_addr = htonl(INADDR_ANY); | 
 |   addr.sin_port = htons(ctx->listen_port); | 
 |  | 
 |   rv = bind(sfd, (struct sockaddr *)&addr, sizeof(addr)); | 
 |   if (rv != 0) { | 
 |     fprintf(stderr, "%s: Failed to bind socket: %s (%d)\n", ctx->display_name, | 
 |             strerror(errno), errno); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   // listen for incoming connections | 
 |   rv = listen(sfd, 1); | 
 |   if (rv != 0) { | 
 |     fprintf(stderr, "%s: Failed to listen on socket: %s (%d)\n", | 
 |             ctx->display_name, strerror(errno), errno); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   ctx->sfd = sfd; | 
 |   assert(ctx->sfd > 0); | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | /** | 
 |  * Accept an incoming connection from a client (nonblocking) | 
 |  * | 
 |  * The resulting client fd is made non-blocking. | 
 |  * | 
 |  * @param ctx context object | 
 |  * @return 0 on success, any other value indicates an error | 
 |  */ | 
 | static int client_tryaccept(struct tcp_server_ctx *ctx) { | 
 |   int rv; | 
 |  | 
 |   assert(ctx->sfd > 0); | 
 |   assert(ctx->cfd == 0); | 
 |  | 
 |   int cfd = accept(ctx->sfd, NULL, NULL); | 
 |  | 
 |   if (cfd == -1 && errno == EAGAIN) { | 
 |     return -EAGAIN; | 
 |   } | 
 |  | 
 |   if (cfd == -1) { | 
 |     fprintf(stderr, "%s: Unable to accept incoming connection: %s (%d)\n", | 
 |             ctx->display_name, strerror(errno), errno); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   rv = fcntl(cfd, F_SETFL, O_NONBLOCK); | 
 |   if (rv != 0) { | 
 |     fprintf(stderr, "%s: Unable to make client socket non-blocking: %s (%d)\n", | 
 |             ctx->display_name, strerror(errno), errno); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   ctx->cfd = cfd; | 
 |   assert(ctx->cfd > 0); | 
 |  | 
 |   printf("%s: Accepted client connection\n", ctx->display_name); | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | /** | 
 |  * Stop the TCP server | 
 |  * | 
 |  * @param ctx context object | 
 |  */ | 
 | static void stop(struct tcp_server_ctx *ctx) { | 
 |   assert(ctx); | 
 |   if (!ctx->sfd) { | 
 |     return; | 
 |   } | 
 |   close(ctx->sfd); | 
 |   ctx->sfd = 0; | 
 | } | 
 |  | 
 | /** | 
 |  * Receive a byte from a connected client | 
 |  * | 
 |  * @param ctx context object | 
 |  * @param cmd byte received | 
 |  * @return true if a byte was read | 
 |  */ | 
 | static bool get_byte(struct tcp_server_ctx *ctx, char *cmd) { | 
 |   assert(ctx); | 
 |  | 
 |   ssize_t num_read = read(ctx->cfd, cmd, 1); | 
 |  | 
 |   if (num_read == 0) { | 
 |     return false; | 
 |   } | 
 |   if (num_read == -1) { | 
 |     if (errno == EAGAIN || errno == EWOULDBLOCK) { | 
 |       return false; | 
 |     } else if (errno == EBADF) { | 
 |       // Possibly client went away? Accept a new connection. | 
 |       fprintf(stderr, "%s: Client disappeared.\n", ctx->display_name); | 
 |       tcp_server_client_close(ctx); | 
 |       return false; | 
 |     } else { | 
 |       fprintf(stderr, "%s: Error while reading from client: %s (%d)\n", | 
 |               ctx->display_name, strerror(errno), errno); | 
 |       assert(0 && "Error reading from client"); | 
 |     } | 
 |   } | 
 |   assert(num_read == 1); | 
 |   return true; | 
 | } | 
 |  | 
 | /** | 
 |  * Send a byte to a connected client | 
 |  * | 
 |  * @param ctx context object | 
 |  * @param cmd byte to send | 
 |  */ | 
 | static void put_byte(struct tcp_server_ctx *ctx, char cmd) { | 
 |   while (1) { | 
 |     ssize_t num_written = send(ctx->cfd, &cmd, sizeof(cmd), MSG_NOSIGNAL); | 
 |     if (num_written == -1) { | 
 |       if (errno == EAGAIN || errno == EWOULDBLOCK) { | 
 |         continue; | 
 |       } else if (errno == EPIPE) { | 
 |         printf("%s: Remote disconnected.\n", ctx->display_name); | 
 |         tcp_server_client_close(ctx); | 
 |         break; | 
 |       } else { | 
 |         fprintf(stderr, "%s: Error while writing to client: %s (%d)\n", | 
 |                 ctx->display_name, strerror(errno), errno); | 
 |         assert(0 && "Error writing to client."); | 
 |       } | 
 |     } | 
 |     if (num_written >= 1) { | 
 |       break; | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | /** | 
 |  * Cleanup server context | 
 |  * | 
 |  * @param ctx context object | 
 |  */ | 
 | static void ctx_free(struct tcp_server_ctx *ctx) { | 
 |   // Free the buffers | 
 |   tcp_buffer_free(&ctx->buf_in); | 
 |   tcp_buffer_free(&ctx->buf_out); | 
 |   // Free the display name | 
 |   free(ctx->display_name); | 
 |   // Free the ctx | 
 |   free(ctx); | 
 |   ctx = NULL; | 
 | } | 
 |  | 
 | /** | 
 |  * Thread function to create a new server instance | 
 |  * | 
 |  * @param ctx_void context object | 
 |  * @return Always returns NULL | 
 |  */ | 
 | static void *server_create(void *ctx_void) { | 
 |   // Cast to a server struct | 
 |   struct tcp_server_ctx *ctx = (struct tcp_server_ctx *)ctx_void; | 
 |   struct timeval timeout; | 
 |  | 
 |   // Start the server | 
 |   int rv = start(ctx); | 
 |   if (rv != 0) { | 
 |     fprintf(stderr, "%s: Unable to create TCP server on port %d\n", | 
 |             ctx->display_name, ctx->listen_port); | 
 |     goto err_cleanup_return; | 
 |   } | 
 |  | 
 |   // Initialise timeout | 
 |   timeout.tv_sec = 0; | 
 |  | 
 |   // Initialise fd_set | 
 |  | 
 |   // Start waiting for connection / data | 
 |   char xfer_data; | 
 |   while (ctx->socket_run) { | 
 |     // Initialise structure of fds | 
 |     fd_set read_fds; | 
 |     FD_ZERO(&read_fds); | 
 |     if (ctx->sfd) { | 
 |       FD_SET(ctx->sfd, &read_fds); | 
 |     } | 
 |     if (ctx->cfd) { | 
 |       FD_SET(ctx->cfd, &read_fds); | 
 |     } | 
 |     // max fd num | 
 |     int mfd = (ctx->cfd > ctx->sfd) ? ctx->cfd : ctx->sfd; | 
 |  | 
 |     // Set timeout - 50us gives good performance | 
 |     timeout.tv_usec = 50; | 
 |  | 
 |     // Wait for socket activity or timeout | 
 |     rv = select(mfd + 1, &read_fds, NULL, NULL, &timeout); | 
 |  | 
 |     if (rv < 0) { | 
 |       printf("%s: Socket read failed, port: %d\n", ctx->display_name, | 
 |              ctx->listen_port); | 
 |       tcp_server_client_close(ctx); | 
 |     } | 
 |  | 
 |     // New connection | 
 |     if (FD_ISSET(ctx->sfd, &read_fds)) { | 
 |       client_tryaccept(ctx); | 
 |     } | 
 |  | 
 |     // New client data | 
 |     if (FD_ISSET(ctx->cfd, &read_fds)) { | 
 |       while (get_byte(ctx, &xfer_data)) { | 
 |         tcp_buffer_put_byte(ctx->buf_in, xfer_data); | 
 |       } | 
 |     } | 
 |  | 
 |     if (ctx->cfd != 0) { | 
 |       while (tcp_buffer_get_byte(ctx->buf_out, &xfer_data)) { | 
 |         put_byte(ctx, xfer_data); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 | err_cleanup_return: | 
 |  | 
 |   // Simulation done - clean up | 
 |   tcp_server_client_close(ctx); | 
 |   stop(ctx); | 
 |  | 
 |   return NULL; | 
 | } | 
 |  | 
 | // Abstract interface functions | 
 | tcp_server_ctx *tcp_server_create(const char *display_name, int listen_port) { | 
 |   struct tcp_server_ctx *ctx = | 
 |       (struct tcp_server_ctx *)calloc(1, sizeof(struct tcp_server_ctx)); | 
 |   assert(ctx); | 
 |  | 
 |   // Create the buffers | 
 |   struct tcp_buf *buf_in = tcp_buffer_new(); | 
 |   struct tcp_buf *buf_out = tcp_buffer_new(); | 
 |   assert(buf_in); | 
 |   assert(buf_out); | 
 |  | 
 |   // Populate the struct with buffer pointers | 
 |   ctx->buf_in = buf_in; | 
 |   ctx->buf_out = buf_out; | 
 |  | 
 |   // Set up socket details | 
 |   ctx->socket_run = true; | 
 |   ctx->listen_port = listen_port; | 
 |   ctx->display_name = strdup(display_name); | 
 |   assert(ctx->display_name); | 
 |  | 
 |   if (pthread_create(&ctx->sock_thread, NULL, server_create, (void *)ctx) != | 
 |       0) { | 
 |     fprintf(stderr, "%s: Unable to create TCP socket thread\n", | 
 |             ctx->display_name); | 
 |     ctx_free(ctx); | 
 |     free(ctx); | 
 |     return NULL; | 
 |   } | 
 |   return ctx; | 
 | } | 
 |  | 
 | bool tcp_server_read(struct tcp_server_ctx *ctx, char *dat) { | 
 |   return tcp_buffer_get_byte(ctx->buf_in, dat); | 
 | } | 
 |  | 
 | void tcp_server_write(struct tcp_server_ctx *ctx, char dat) { | 
 |   tcp_buffer_put_byte(ctx->buf_out, dat); | 
 | } | 
 |  | 
 | void tcp_server_close(struct tcp_server_ctx *ctx) { | 
 |   // Shut down the socket thread | 
 |   ctx->socket_run = false; | 
 |   pthread_join(ctx->sock_thread, NULL); | 
 |   ctx_free(ctx); | 
 | } | 
 |  | 
 | void tcp_server_client_close(struct tcp_server_ctx *ctx) { | 
 |   assert(ctx); | 
 |  | 
 |   if (!ctx->cfd) { | 
 |     return; | 
 |   } | 
 |  | 
 |   close(ctx->cfd); | 
 |   ctx->cfd = 0; | 
 | } |