|  | // 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; | 
|  | } |