+struct semihosting_tcp_service {
+ struct semihosting *semihosting;
+ char *name;
+ int error;
+};
+
+static bool semihosting_is_redirected(struct semihosting *semihosting, int fd)
+{
+ if (semihosting->redirect_cfg == SEMIHOSTING_REDIRECT_CFG_NONE)
+ return false;
+
+ bool is_read_op = false;
+
+ switch (semihosting->op) {
+ /* check debug semihosting operations: READC, WRITEC and WRITE0 */
+ case SEMIHOSTING_SYS_READC:
+ is_read_op = true;
+ /* fall through */
+ case SEMIHOSTING_SYS_WRITEC:
+ case SEMIHOSTING_SYS_WRITE0:
+ /* debug operations are redirected when CFG is either DEBUG or ALL */
+ if (semihosting->redirect_cfg == SEMIHOSTING_REDIRECT_CFG_STDIO)
+ return false;
+ break;
+
+ /* check stdio semihosting operations: READ and WRITE */
+ case SEMIHOSTING_SYS_READ:
+ is_read_op = true;
+ /* fall through */
+ case SEMIHOSTING_SYS_WRITE:
+ /* stdio operations are redirected when CFG is either STDIO or ALL */
+ if (semihosting->redirect_cfg == SEMIHOSTING_REDIRECT_CFG_DEBUG)
+ return false;
+ break;
+
+ default:
+ return false;
+ }
+
+ if (is_read_op)
+ return fd == semihosting->stdin_fd;
+
+ /* write operation */
+ return fd == semihosting->stdout_fd || fd == semihosting->stderr_fd;
+}
+
+static ssize_t semihosting_redirect_write(struct semihosting *semihosting, void *buf, int size)
+{
+ if (!semihosting->tcp_connection) {
+ LOG_ERROR("No connected TCP client for semihosting");
+ semihosting->sys_errno = EBADF; /* Bad file number */
+ return -1;
+ }
+
+ struct semihosting_tcp_service *service = semihosting->tcp_connection->service->priv;
+
+ int retval = connection_write(semihosting->tcp_connection, buf, size);
+
+ if (retval < 0)
+ log_socket_error(service->name);
+
+ return retval;
+}
+
+static ssize_t semihosting_write(struct semihosting *semihosting, int fd, void *buf, int size)
+{
+ if (semihosting_is_redirected(semihosting, fd))
+ return semihosting_redirect_write(semihosting, buf, size);
+
+ /* default write */
+ return write(fd, buf, size);
+}
+
+static ssize_t semihosting_redirect_read(struct semihosting *semihosting, void *buf, int size)
+{
+ if (!semihosting->tcp_connection) {
+ LOG_ERROR("No connected TCP client for semihosting");
+ semihosting->sys_errno = EBADF; /* Bad file number */
+ return -1;
+ }
+
+ struct semihosting_tcp_service *service = semihosting->tcp_connection->service->priv;
+
+ service->error = ERROR_OK;
+ semihosting->tcp_connection->input_pending = true;
+
+ int retval = connection_read(semihosting->tcp_connection, buf, size);
+
+ if (retval <= 0)
+ service->error = ERROR_SERVER_REMOTE_CLOSED;
+
+ if (retval < 0)
+ log_socket_error(service->name);
+
+ semihosting->tcp_connection->input_pending = false;
+
+ return retval;
+}
+
+static inline int semihosting_putchar(struct semihosting *semihosting, int fd, int c)
+{
+ if (semihosting_is_redirected(semihosting, fd))
+ return semihosting_redirect_write(semihosting, &c, 1);
+
+ /* default putchar */
+ return putchar(c);
+}
+
+static inline ssize_t semihosting_read(struct semihosting *semihosting, int fd, void *buf, int size)
+{
+ if (semihosting_is_redirected(semihosting, fd))
+ return semihosting_redirect_read(semihosting, buf, size);
+
+ /* default read */
+ ssize_t result = read(fd, buf, size);
+ semihosting->sys_errno = errno;
+
+ return result;
+}
+
+static inline int semihosting_getchar(struct semihosting *semihosting, int fd)
+{
+ if (semihosting_is_redirected(semihosting, fd)) {
+ unsigned char c;
+
+ if (semihosting_redirect_read(semihosting, &c, 1) > 0)
+ return c;
+
+ return EOF;
+ }
+
+ /* default getchar */
+ return getchar();
+}
+