From 69b0ee6d68fe000c2e71714024b996b592168119 Mon Sep 17 00:00:00 2001 From: KubaPro010 Date: Sun, 3 May 2026 17:12:22 +0200 Subject: [PATCH] ipc test for fm95 --- CMakeLists.txt | 8 ++--- io/ipc.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++ io/ipc.h | 19 ++++++++++ src/fm95.c | 31 ++++++++++++++++ 4 files changed, 150 insertions(+), 4 deletions(-) create mode 100644 io/ipc.c create mode 100644 io/ipc.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 6410556..689f005 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,13 +44,13 @@ foreach(SRC_FILE ${SRC_FILES}) target_compile_options(${EXEC_NAME} PRIVATE -O2 -Wall -Wextra -Werror -Wno-unused-parameter) if(EXEC_NAME STREQUAL "fm95") - target_link_libraries(${EXEC_NAME} PRIVATE libfmfilter libfmmodulation libfmdsp libfmio pulse pulse-simple m liquid inih) + target_link_libraries(${EXEC_NAME} PRIVATE libfmfilter libfmmodulation libfmdsp libfmio pulse pulse-simple m liquid inih pthread) elseif(EXEC_NAME STREQUAL "chimer95") - target_link_libraries(${EXEC_NAME} PRIVATE libfmdsp inih m libfmio pulse pulse-simple liquid) + target_link_libraries(${EXEC_NAME} PRIVATE libfmdsp inih m libfmio pulse pulse-simple liquid pthread) elseif(EXEC_NAME STREQUAL "sca95") - target_link_libraries(${EXEC_NAME} PRIVATE libfmmodulation inih m libfmio pulse pulse-simple libfmdsp liquid) + target_link_libraries(${EXEC_NAME} PRIVATE libfmmodulation inih m libfmio pulse pulse-simple libfmdsp liquid pthread) elseif(EXEC_NAME STREQUAL "vban95") - target_link_libraries(${EXEC_NAME} PRIVATE libfmio pulse pulse-simple liquid) + target_link_libraries(${EXEC_NAME} PRIVATE libfmio pulse pulse-simple liquid pthread) else() message(FATAL_ERROR "How do I link this? ${EXEC_NAME}") endif() diff --git a/io/ipc.c b/io/ipc.c new file mode 100644 index 0000000..4dc1c37 --- /dev/null +++ b/io/ipc.c @@ -0,0 +1,96 @@ +#include "ipc.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +static int make_server_socket(const char *path) +{ + int fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) { perror("ipc: socket"); return -1; } + + unlink(path); + + struct sockaddr_un addr = { .sun_family = AF_UNIX }; + strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); + + if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("ipc: bind"); close(fd); return -1; + } + if (listen(fd, IPC_BACKLOG) < 0) { + perror("ipc: listen"); close(fd); return -1; + } + return fd; +} + +static void *listener_thread(void *arg) { + ipc_ctx_t *ctx = (ipc_ctx_t *)arg; + + while (ctx->running) { + struct sockaddr_un peer; + socklen_t peer_len = sizeof(peer); + + int client_fd = accept(ctx->server_fd, + (struct sockaddr *)&peer, &peer_len); + if (client_fd < 0) { + if (errno == EINVAL || errno == EBADF) break; /* shutting down */ + if (errno == EINTR) continue; + perror("ipc: accept"); + continue; + } + + printf("[ipc] new client fd=%d\n", client_fd); + + int *fd_ptr = malloc(sizeof(int)); + if (!fd_ptr) { close(client_fd); continue; } + *fd_ptr = client_fd; + + pthread_t tid; + if (pthread_create(&tid, NULL, ctx->handler, fd_ptr) != 0) { + perror("ipc: pthread_create"); + free(fd_ptr); + close(client_fd); + continue; + } + pthread_detach(tid); + } + + printf("[ipc] listener exiting\n"); + return NULL; +} + +int create_ipc(ipc_ctx_t *ctx, ipc_client_fn handler, const char* socket_path) { + ctx->handler = handler; + ctx->running = 1; + ctx->server_fd = make_server_socket(socket_path); + ctx->socket_path = strdup(socket_path); + if (ctx->server_fd < 0) return -1; + + pthread_t tid; + if (pthread_create(&tid, NULL, listener_thread, ctx) != 0) { + perror("ipc: pthread_create"); + close(ctx->server_fd); + unlink(socket_path); + return -1; + } + + /* store tid so destroy_ipc can join it */ + ctx->_tid = tid; + + printf("[ipc] listening on %s\n", socket_path); + return 0; +} + +void destroy_ipc(ipc_ctx_t *ctx) { + ctx->running = 0; + shutdown(ctx->server_fd, SHUT_RDWR); /* unblock accept() */ + close(ctx->server_fd); + unlink(ctx->socket_path); + pthread_join(ctx->_tid, NULL); + printf("[ipc] shut down\n"); +} \ No newline at end of file diff --git a/io/ipc.h b/io/ipc.h new file mode 100644 index 0000000..15ecdfd --- /dev/null +++ b/io/ipc.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +#define IPC_SOCKET_PATH "/tmp/ipc_demo.sock" +#define IPC_BACKLOG 1 + +typedef void *(*ipc_client_fn)(void *client_fd_ptr); + +typedef struct { + int server_fd; + volatile int running; + ipc_client_fn handler; + pthread_t _tid; /* internal — do not touch */ + const char* socket_path; +} ipc_ctx_t; +int create_ipc(ipc_ctx_t *ctx, ipc_client_fn handler, const char* socket_path); +void destroy_ipc(ipc_ctx_t *ctx); \ No newline at end of file diff --git a/src/fm95.c b/src/fm95.c index 61be2cb..a463762 100644 --- a/src/fm95.c +++ b/src/fm95.c @@ -17,6 +17,7 @@ #define BUFFER_SIZE 4998 // This defines how many samples to process at a time, because the loop here is this: get signal -> process signal -> output signal, and when we get signal we actually get BUFFER_SIZE of them #include "../io/audio.h" +#include "../io/ipc.h" static volatile sig_atomic_t to_run = 1; static volatile sig_atomic_t to_reload = 0; @@ -456,9 +457,38 @@ void init_runtime(FM95_Runtime* runtime, const FM95_Config config) { } } +#define BUF_SIZE 256 +static void *handle_client(void *arg) { + int fd = *(int *)arg; + free(arg); + + char buf[BUF_SIZE]; + ssize_t n; + + while ((n = recv(fd, buf, sizeof(buf) - 1, 0)) > 0) { + buf[n] = '\0'; + printf("[client fd=%d] %s\n", fd, buf); + + char reply[BUF_SIZE]; + snprintf(reply, sizeof(reply), "ECHO: %s", buf); + send(fd, reply, strlen(reply), 0); + } + + printf("[client fd=%d] disconnected\n", fd); + close(fd); + return NULL; +} + int main(int argc, char **argv) { printf("fm95 (an FM Processor by radio95) version 2.5\n"); + ipc_ctx_t ctx = NULL; + if(create_ipc(&ctx, handle_client, "/etc/fm95/ctl.socket") < 0) { + printf("Could not create IPC.\n"); + ctx = NULL; + } + + FM95_Config config = { .volumes = { .pilot = 0.09f, @@ -573,5 +603,6 @@ int main(int argc, char **argv) { cleanup_audio_runtime(&runtime, config.options); break; } + if(ctx != NULL) destroy_ipc(&ctx); return ret; }