From d80bb9c6f45816ea8d640151c9ed1e1798638623 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Wed, 21 Aug 2024 09:31:07 -0700 Subject: [PATCH 1/6] separate sps/pps data --- CMakeLists.txt | 6 +++++- app/videodecoder.cpp | 7 ++++--- lib/CMakeLists.txt | 10 ++++++---- lib/gamepad/video.c | 6 ++++-- lib/gamepad/video.h | 7 ------- lib/vanilla.c | 8 -------- lib/vanilla.h | 16 ++++++++-------- 7 files changed, 27 insertions(+), 33 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 96fbd66..058760c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,5 +10,9 @@ SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") SET(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") add_subdirectory(lib) -add_subdirectory(pipe) + +if (NOT ANDROID) + add_subdirectory(pipe) +endif() + add_subdirectory(app) diff --git a/app/videodecoder.cpp b/app/videodecoder.cpp index 24f09da..62bed10 100644 --- a/app/videodecoder.cpp +++ b/app/videodecoder.cpp @@ -171,6 +171,7 @@ void VideoDecoder::startRecording() m_recordingFilename = QDir(QStandardPaths::writableLocation(QStandardPaths::TempLocation)).filePath("vanilla-recording-%0.mp4").arg(QDateTime::currentSecsSinceEpoch()); QByteArray filenameUtf8 = m_recordingFilename.toUtf8(); + size_t sps_pps_size; int r = avformat_alloc_output_context2(&m_recordingCtx, nullptr, nullptr, filenameUtf8.constData()); if (r < 0) { @@ -192,11 +193,11 @@ void VideoDecoder::startRecording() m_videoStream->time_base = {1, 60}; m_videoStream->codecpar->codec_id = AV_CODEC_ID_H264; - size_t sps_pps_size; - vanilla_retrieve_sps_pps_data(nullptr, &sps_pps_size); + sps_pps_size = sizeof(VANILLA_SPS_PARAMS) + sizeof(VANILLA_PPS_PARAMS); m_videoStream->codecpar->extradata_size = sps_pps_size; m_videoStream->codecpar->extradata = (uint8_t *) av_malloc(sps_pps_size); - vanilla_retrieve_sps_pps_data(m_videoStream->codecpar->extradata, &sps_pps_size); + memcpy(m_videoStream->codecpar->extradata, VANILLA_SPS_PARAMS, sizeof(VANILLA_SPS_PARAMS)); + memcpy(m_videoStream->codecpar->extradata + sizeof(VANILLA_SPS_PARAMS), VANILLA_PPS_PARAMS, sizeof(VANILLA_PPS_PARAMS)); m_audioStream = avformat_new_stream(m_recordingCtx, nullptr); if (!m_audioStream) { diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index b999b35..52c2f1c 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -1,4 +1,4 @@ -add_library(vanilla SHARED +add_library(vanilla STATIC gamepad/audio.c gamepad/command.c gamepad/gamepad.c @@ -13,8 +13,10 @@ target_include_directories(vanilla PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ) -target_link_libraries(vanilla PRIVATE - pthread -) +if (NOT ANDROID) + target_link_libraries(vanilla PRIVATE + pthread + ) +endif() install(TARGETS vanilla) diff --git a/lib/gamepad/video.c b/lib/gamepad/video.c index 18d6424..cbf3cc4 100644 --- a/lib/gamepad/video.c +++ b/lib/gamepad/video.c @@ -146,8 +146,10 @@ void handle_video_packet(vanilla_event_handler_t event_handler, void *context, u frame_decode_num++; if (is_idr) { - memcpy(nals_current, sps_pps_params, sizeof(sps_pps_params)); - nals_current += sizeof(sps_pps_params); + memcpy(nals_current, VANILLA_SPS_PARAMS, sizeof(VANILLA_SPS_PARAMS)); + nals_current += sizeof(VANILLA_SPS_PARAMS); + memcpy(nals_current, VANILLA_PPS_PARAMS, sizeof(VANILLA_PPS_PARAMS)); + nals_current += sizeof(VANILLA_PPS_PARAMS); } // begin slice nalu diff --git a/lib/gamepad/video.h b/lib/gamepad/video.h index cf136f3..cc0002e 100644 --- a/lib/gamepad/video.h +++ b/lib/gamepad/video.h @@ -6,11 +6,4 @@ void *listen_video(void *x); void request_idr(); -static const uint8_t sps_pps_params[] = { - // sps - 0x00, 0x00, 0x00, 0x01, 0x67, 0x64, 0x00, 0x20, 0xac, 0x2b, 0x40, 0x6c, 0x1e, 0xf3, 0x68, - // pps - 0x00, 0x00, 0x00, 0x01, 0x68, 0xee, 0x06, 0x0c, 0xe8 -}; - #endif // GAMEPAD_VIDEO_H \ No newline at end of file diff --git a/lib/vanilla.c b/lib/vanilla.c index ce328b0..bf7fbb6 100644 --- a/lib/vanilla.c +++ b/lib/vanilla.c @@ -103,14 +103,6 @@ void vanilla_request_idr() request_idr(); } -void vanilla_retrieve_sps_pps_data(void *data, size_t *size) -{ - if (data != NULL) { - memcpy(data, sps_pps_params, MIN(*size, sizeof(sps_pps_params))); - } - *size = sizeof(sps_pps_params); -} - void vanilla_set_region(int region) { set_region(region); diff --git a/lib/vanilla.h b/lib/vanilla.h index f188edb..8071bfb 100644 --- a/lib/vanilla.h +++ b/lib/vanilla.h @@ -85,6 +85,14 @@ enum VanillaBatteryStatus { VANILLA_BATTERY_STATUS_FULL = 6 }; +static const uint8_t VANILLA_SPS_PARAMS[] = { + 0x00, 0x00, 0x00, 0x01, 0x67, 0x64, 0x00, 0x20, 0xac, 0x2b, 0x40, 0x6c, 0x1e, 0xf3, 0x68, +}; + +static const uint8_t VANILLA_PPS_PARAMS[] = { + 0x00, 0x00, 0x00, 0x01, 0x68, 0xee, 0x06, 0x0c, 0xe8 +}; + /** * Event handler used by caller to receive events */ @@ -142,14 +150,6 @@ void vanilla_install_logger(void (*logger)(const char *, va_list args)); */ void vanilla_request_idr(); -/** - * Retrieve SPS/PPS data for H.264 encoding - * - * If `data` is null, `*size` will be set to the number of bytes required. - * If `data` is not null, bytes will be copied up to `*size` or the total number of bytes. - */ -void vanilla_retrieve_sps_pps_data(void *data, size_t *size); - /** * Sets the region Vanilla should present itself to the console * From 702f637248425bc874d3efb4ffb31e5c41775e55 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Wed, 18 Sep 2024 11:57:38 -0700 Subject: [PATCH 2/6] send sync/command opcodes over UDP so pipe can be controlled by the frontend --- app/backend.cpp | 16 +- app/backend.h | 5 +- app/mainwindow.cpp | 6 +- lib/gamepad/gamepad.c | 95 +++++++-- lib/gamepad/gamepad.h | 1 + lib/gamepad/input.c | 1 + lib/vanilla.c | 5 + lib/vanilla.h | 2 + pipe/README.md | 5 + pipe/{linux => }/def.h | 9 +- pipe/linux/main.c | 74 +++---- pipe/linux/wpa.c | 457 ++++++++++++++++++++++------------------- pipe/linux/wpa.h | 10 +- 13 files changed, 388 insertions(+), 298 deletions(-) create mode 100644 pipe/README.md rename pipe/{linux => }/def.h (51%) diff --git a/app/backend.cpp b/app/backend.cpp index 43b491f..6536f4a 100644 --- a/app/backend.cpp +++ b/app/backend.cpp @@ -55,6 +55,13 @@ void BackendViaLocalRoot::requestIDR() vanilla_request_idr(); } +void BackendViaLocalRoot::sync(uint16_t code) +{ + auto watcher = new QFutureWatcher(); + connect(watcher, &QFutureWatcher::finished, this, &BackendViaLocalRoot::syncFutureCompleted); + watcher->setFuture(QtConcurrent::run(vanilla_sync, code, m_serverAddress.toIPv4Address())); +} + void BackendViaLocalRoot::connectToConsole() { QtConcurrent::run(connectInternal, this, m_serverAddress); @@ -113,14 +120,9 @@ BackendPipe::~BackendPipe() quit(); } -void BackendPipe::sync(uint16_t code) +void BackendPipe::start() { - m_process->start(QStringLiteral("pkexec"), {pipeProcessFilename(), m_wirelessInterface, QStringLiteral("-sync"), QString::number(code)}); -} - -void BackendPipe::connectToConsole() -{ - m_process->start(QStringLiteral("pkexec"), {pipeProcessFilename(), m_wirelessInterface, QStringLiteral("-connect")}); + m_process->start(QStringLiteral("pkexec"), {pipeProcessFilename(), m_wirelessInterface}); } QString BackendPipe::pipeProcessFilename() diff --git a/app/backend.h b/app/backend.h index d8c6849..c640963 100644 --- a/app/backend.h +++ b/app/backend.h @@ -18,8 +18,7 @@ public: virtual ~BackendPipe() override; public slots: - void sync(uint16_t code); - void connectToConsole(); + void start(); void quit(); signals: @@ -65,6 +64,7 @@ signals: public slots: // These slots must be called with Qt::QueuedConnection to start the event loops in the backend's thread virtual void init(); + virtual void sync(uint16_t code) = 0; virtual void connectToConsole() = 0; }; @@ -83,6 +83,7 @@ public: virtual void setBatteryStatus(int status) override; public slots: + virtual void sync(uint16_t code) override; virtual void connectToConsole() override; private: diff --git a/app/mainwindow.cpp b/app/mainwindow.cpp index bc05e5d..36a52c6 100644 --- a/app/mainwindow.cpp +++ b/app/mainwindow.cpp @@ -367,6 +367,8 @@ void MainWindow::initBackend(T func) // m_backend = new BackendViaLocalRoot(QHostAddress()); } else { m_pipe = new BackendPipe(localWirelessIntf, this); + m_pipe->start(); + m_backend = new BackendViaLocalRoot(QHostAddress::LocalHost); } @@ -411,10 +413,6 @@ void MainWindow::setConnectedState(bool on) m_connectBtn->setText(on ? tr("Disconnect") : tr("Connect")); if (on) { initBackend([this]{ - if (m_pipe) { - m_pipe->connectToConsole(); - } - QMetaObject::invokeMethod(m_backend, &Backend::connectToConsole, Qt::QueuedConnection); updateVolumeAxis(); diff --git a/lib/gamepad/gamepad.c b/lib/gamepad/gamepad.c index b571cda..173f123 100644 --- a/lib/gamepad/gamepad.c +++ b/lib/gamepad/gamepad.c @@ -16,7 +16,7 @@ #include "input.h" #include "video.h" -#include "../pipe/linux/def.h" +#include "../pipe/def.h" #include "status.h" #include "util.h" @@ -52,7 +52,7 @@ void send_to_console(int fd, const void *data, size_t data_size, int port) ssize_t sent = sendto(fd, data, data_size, 0, (const struct sockaddr *) &address, sizeof(address)); if (sent == -1) { - print_info("Failed to send to Wii U socket: fd - %d; port - %d", fd, port); + print_info("Failed to send to Wii U socket: fd - %d; port - %d", fd, port - 100); } } @@ -97,17 +97,85 @@ int send_pipe_cc(int skt, uint32_t cc, int wait_for_reply) do { sendto(skt, &send_cc, sizeof(send_cc), 0, (struct sockaddr *) &addr, sizeof(addr)); - if (wait_for_reply) { - read_size = recv(skt, &recv_cc, sizeof(recv_cc), 0); - if (read_size == sizeof(recv_cc) && ntohl(recv_cc) == VANILLA_PIPE_CC_BIND_ACK) { - return 1; - } + if (1 || !wait_for_reply) { + return 1; + } + + read_size = recv(skt, &recv_cc, sizeof(recv_cc), 0); + if (read_size == sizeof(recv_cc) && ntohl(recv_cc) == VANILLA_PIPE_CC_BIND_ACK) { + return 1; } } while (!is_interrupted()); return 0; } +int connect_to_backend(int *socket, uint32_t cc) +{ + // Try to bind with backend + int pipe_cc_skt; + if (!create_socket(&pipe_cc_skt, VANILLA_PIPE_CMD_CLIENT_PORT)) { + return VANILLA_ERROR; + } + + struct timeval tv = {0}; + tv.tv_sec = 2; + setsockopt(pipe_cc_skt, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + + if (!send_pipe_cc(pipe_cc_skt, cc, 1)) { + print_info("FAILED TO BIND TO PIPE"); + close(pipe_cc_skt); + return VANILLA_ERROR; + } + + *socket = pipe_cc_skt; + + return VANILLA_SUCCESS; +} + +int sync_internal(uint16_t code, uint32_t server_address) +{ + clear_interrupt(); + + if (server_address == 0) { + SERVER_ADDRESS = inet_addr("192.168.1.10"); + } else { + SERVER_ADDRESS = htonl(server_address); + } + + int skt; + int ret = connect_to_backend(&skt, VANILLA_PIPE_SYNC_CODE(code)); + if (ret != VANILLA_SUCCESS) { + goto exit; + } + + ret = VANILLA_ERROR; + + // Wait for sync result from pipe + uint32_t recv_cc; + while (1) { + ssize_t read_size = recv(skt, &recv_cc, sizeof(recv_cc), 0); + if (read_size == sizeof(recv_cc)) { + recv_cc = ntohl(recv_cc); + if (recv_cc >> 8 == VANILLA_PIPE_CC_SYNC_STATUS >> 8) { + ret = recv_cc & 0xFF; + break; + } + } + + if (is_interrupted()) { + send_pipe_cc(skt, VANILLA_PIPE_CC_UNBIND, 0); + break; + } + } + +exit_pipe: + close(skt); + +exit: + return ret; +} + int connect_as_gamepad_internal(vanilla_event_handler_t event_handler, void *context, uint32_t server_address) { clear_interrupt(); @@ -135,17 +203,10 @@ int connect_as_gamepad_internal(vanilla_event_handler_t event_handler, void *con int ret = VANILLA_ERROR; - // Try to bind with backend int pipe_cc_skt; - if (!create_socket(&pipe_cc_skt, VANILLA_PIPE_CMD_CLIENT_PORT)) goto exit; - - struct timeval tv = {0}; - tv.tv_sec = 2; - setsockopt(pipe_cc_skt, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); - - if (!send_pipe_cc(pipe_cc_skt, VANILLA_PIPE_CC_BIND, 1)) { - print_info("FAILED TO BIND TO PIPE"); - goto exit_pipe; + ret = connect_to_backend(&pipe_cc_skt, VANILLA_PIPE_CC_CONNECT); + if (ret != VANILLA_SUCCESS) { + goto exit; } // Open all required sockets diff --git a/lib/gamepad/gamepad.h b/lib/gamepad/gamepad.h index 9acf235..ddcdb36 100644 --- a/lib/gamepad/gamepad.h +++ b/lib/gamepad/gamepad.h @@ -25,6 +25,7 @@ struct gamepad_thread_context int socket_cmd; }; +int sync_internal(uint16_t code, uint32_t server_address); int connect_as_gamepad_internal(vanilla_event_handler_t event_handler, void *context, uint32_t server_address); unsigned int reverse_bits(unsigned int b, int bit_count); void send_to_console(int fd, const void *data, size_t data_size, int port); diff --git a/lib/gamepad/input.c b/lib/gamepad/input.c index fd01f3e..c405676 100644 --- a/lib/gamepad/input.c +++ b/lib/gamepad/input.c @@ -9,6 +9,7 @@ #include #include "gamepad.h" +#include "status.h" #include "vanilla.h" #include "util.h" diff --git a/lib/vanilla.c b/lib/vanilla.c index bf7fbb6..d33f617 100644 --- a/lib/vanilla.c +++ b/lib/vanilla.c @@ -111,4 +111,9 @@ void vanilla_set_region(int region) void vanilla_set_battery_status(int battery_status) { set_battery_status(battery_status); +} + +int vanilla_sync(uint16_t code, uint32_t server_address) +{ + return sync_internal(code, server_address); } \ No newline at end of file diff --git a/lib/vanilla.h b/lib/vanilla.h index 8071bfb..a00ebdf 100644 --- a/lib/vanilla.h +++ b/lib/vanilla.h @@ -104,6 +104,8 @@ typedef void (*vanilla_event_handler_t)(void *context, int event_type, const cha int vanilla_start(vanilla_event_handler_t event_handler, void *context); int vanilla_start_udp(vanilla_event_handler_t event_handler, void *context, uint32_t server_address); +int vanilla_sync(uint16_t code, uint32_t server_address); + /** * Attempt to stop the current action * diff --git a/pipe/README.md b/pipe/README.md new file mode 100644 index 0000000..dd3f6cb --- /dev/null +++ b/pipe/README.md @@ -0,0 +1,5 @@ +# `vanilla-pipe` + +No, this is not something you smoke, `vanilla-pipe` is a program on various platforms that facilitates a connection between the Wii U and another device. Since the Wii U connection is slightly nonstandard, not all devices can connect to it on their own. Hence, `vanilla-pipe`, which allows a connection on one device that forwards all communication to another. + +`vanilla-pipe` is also used locally to allow the connection to happen with root permissions (which the nonstandard connection requires) while the frontend can remain a user program (with regular access to the user's X/Wayland/PulseAudio sessions). diff --git a/pipe/linux/def.h b/pipe/def.h similarity index 51% rename from pipe/linux/def.h rename to pipe/def.h index e2563cd..032a782 100644 --- a/pipe/linux/def.h +++ b/pipe/def.h @@ -1,12 +1,15 @@ #ifndef VANILLA_PIPE_DEF_H #define VANILLA_PIPE_DEF_H -#include - #define VANILLA_PIPE_CMD_SERVER_PORT 51000 #define VANILLA_PIPE_CMD_CLIENT_PORT 51001 -#define VANILLA_PIPE_CC_BIND 0x56414249 +#define VANILLA_PIPE_CC_SYNC 0x56530000 +#define VANILLA_PIPE_CC_CONNECT 0x56414249 #define VANILLA_PIPE_CC_BIND_ACK 0x56414245 +#define VANILLA_PIPE_CC_SYNC_STATUS 0x53595300 +#define VANILLA_PIPE_CC_BUSY 0x56414255 #define VANILLA_PIPE_CC_UNBIND 0x5641554E +#define VANILLA_PIPE_SYNC_CODE(x) (VANILLA_PIPE_CC_SYNC | (x & 0xFFFF)) + #endif // VANILLA_PIPE_DEF_H \ No newline at end of file diff --git a/pipe/linux/main.c b/pipe/linux/main.c index 5da4150..ebe550a 100644 --- a/pipe/linux/main.c +++ b/pipe/linux/main.c @@ -7,61 +7,33 @@ int main(int argc, const char **argv) { - if (argc < 3) { - goto show_help; + if (argc < 2) { + pprint("vanilla-pipe - brokers a connection between Vanilla and the Wii U\n"); + pprint("\n"); + pprint("Usage: %s [args]\n", argv[0]); + pprint("\n"); + pprint("Modes: \n"); + pprint(" -sync Sync/authenticate with the Wii U.\n"); + pprint(" -connect Connect to the Wii U (requires syncing prior).\n"); + pprint(" -is_synced Returns 1 if gamepad has been synced or 0 if it hasn't yet.\n"); + pprint("\n"); + pprint("Sync code is a 4-digit PIN based on the card suits shown on the console.\n\n"); + pprint(" To calculate the code, use the following:\n"); + pprint("\n"); + pprint(" ♠ (spade) = 0\n"); + pprint(" ♥ (heart) = 1\n"); + pprint(" ♦ (diamond) = 2\n"); + pprint(" ♣ (clover) = 3\n"); + pprint("\n"); + pprint(" Example: ♣♠♥♦ (clover, spade, heart, diamond) would equal 3012\n"); + pprint("\n"); + + return 1; } const char *wireless_interface = argv[1]; - const char *mode = argv[2]; - if (!strcmp("-sync", mode)) { - if (argc < 4) { - pprint("ERROR: -sync requires sync code\n\n"); - goto show_help; - } - - int code = atoi(argv[3]); - if (code == 0) { - pprint("ERROR: Invalid sync code\n\n"); - goto show_help; - } - - vanilla_sync_with_console(wireless_interface, code); - } else if (!strcmp("-connect", mode)) { - vanilla_connect_to_console(wireless_interface); - } else if (!strcmp("-is_synced", mode)) { - if (vanilla_has_config()) { - pprint("YES\n"); - } else { - pprint("NO\n"); - } - } else { - pprint("ERROR: Invalid mode\n\n"); - goto show_help; - } + vanilla_listen(wireless_interface); return 0; - -show_help: - pprint("vanilla-pipe - brokers a connection between Vanilla and the Wii U\n"); - pprint("\n"); - pprint("Usage: %s [args]\n", argv[0]); - pprint("\n"); - pprint("Modes: \n"); - pprint(" -sync Sync/authenticate with the Wii U.\n"); - pprint(" -connect Connect to the Wii U (requires syncing prior).\n"); - pprint(" -is_synced Returns 1 if gamepad has been synced or 0 if it hasn't yet.\n"); - pprint("\n"); - pprint("Sync code is a 4-digit PIN based on the card suits shown on the console.\n\n"); - pprint(" To calculate the code, use the following:\n"); - pprint("\n"); - pprint(" ♠ (spade) = 0\n"); - pprint(" ♥ (heart) = 1\n"); - pprint(" ♦ (diamond) = 2\n"); - pprint(" ♣ (clover) = 3\n"); - pprint("\n"); - pprint(" Example: ♣♠♥♦ (clover, spade, heart, diamond) would equal 3012\n"); - pprint("\n"); - - return 1; } \ No newline at end of file diff --git a/pipe/linux/wpa.c b/pipe/linux/wpa.c index 6a20a48..095c948 100644 --- a/pipe/linux/wpa.c +++ b/pipe/linux/wpa.c @@ -17,7 +17,7 @@ #include #include -#include "def.h" +#include "../def.h" #include "ports.h" #include "status.h" #include "util.h" @@ -27,7 +27,13 @@ const char *wpa_ctrl_interface = "/var/run/wpa_supplicant_drc"; pthread_mutex_t running_mutex; +pthread_mutex_t main_loop_mutex; +pthread_mutex_t action_mutex; +pthread_mutex_t sync_mutex; int running = 0; +int main_loop = 0; +int sync_result_ready = 0; +uint8_t sync_result = 0; typedef struct { int from_socket; @@ -38,8 +44,16 @@ typedef struct { } relay_ports; struct in_addr client_address = {0}; -pthread_mutex_t client_address_mutex; -pthread_cond_t client_address_waitcond; + +struct sync_args { + const char *wireless_interface; + const char *wireless_config; + uint16_t code; + void *(*start_routine)(void *); + struct wpa_ctrl *ctrl; +}; + +#define THREADRESULT(x) ((void *) (uintptr_t) (x)) void lpprint(const char *fmt, va_list args) { @@ -67,12 +81,10 @@ void print_info(const char *errstr, ...) int is_interrupted() { - return !running; -} - -void clear_interrupt() -{ - running = 1; + pthread_mutex_lock(&running_mutex); + int r = !running; + pthread_mutex_unlock(&running_mutex); + return r; } void wpa_msg(char *msg, size_t len) @@ -282,15 +294,20 @@ give_up: void quit_loop() { pthread_mutex_lock(&running_mutex); - pthread_mutex_lock(&client_address_mutex); + pthread_mutex_lock(&main_loop_mutex); running = 0; - pthread_cond_broadcast(&client_address_waitcond); - pthread_mutex_unlock(&client_address_mutex); + main_loop = 0; + pthread_mutex_unlock(&main_loop_mutex); pthread_mutex_unlock(&running_mutex); } void sigint_handler(int signum) { + if (signum == SIGINT) { + pprint("RECEIVED INTERRUPT SIGNAL\n"); + } else if (signum == SIGTERM) { + pprint("RECEIVED TERMINATE SIGNAL\n"); + } quit_loop(); signal(signum, SIG_DFL); } @@ -315,38 +332,31 @@ void *read_stdin(void *) return NULL; } -int wpa_setup_environment(const char *wireless_interface, const char *wireless_conf_file, ready_callback_t callback, void *callback_data) +void *wpa_setup_environment(void *data) { - int ret = VANILLA_ERROR; + void *ret = THREADRESULT(VANILLA_ERROR); - clear_interrupt(); - - signal(SIGINT, sigint_handler); - signal(SIGTERM, sigint_handler); - //install_interrupt_handler(); - - pthread_t stdin_thread; - pthread_create(&stdin_thread, NULL, read_stdin, NULL); + struct sync_args *args = (struct sync_args *) data; // Check status of interface with NetworkManager int is_managed = 0; - if (is_networkmanager_managing_device(wireless_interface, &is_managed) != VANILLA_SUCCESS) { + if (is_networkmanager_managing_device(args->wireless_interface, &is_managed) != VANILLA_SUCCESS) { print_info("FAILED TO DETERMINE MANAGED STATE OF WIRELESS INTERFACE"); //goto die; } // If NetworkManager is managing this device, temporarily stop it from doing so if (is_managed) { - if (disable_networkmanager_on_device(wireless_interface) != VANILLA_SUCCESS) { + if (disable_networkmanager_on_device(args->wireless_interface) != VANILLA_SUCCESS) { print_info("FAILED TO SET %s TO UNMANAGED, RESULTS MAY BE UNPREDICTABLE"); } else { - print_info("TEMPORARILY SET %s TO UNMANAGED", wireless_interface); + print_info("TEMPORARILY SET %s TO UNMANAGED", args->wireless_interface); } } // Start modified WPA supplicant pid_t pid; - int err = start_wpa_supplicant(wireless_interface, wireless_conf_file, &pid); + int err = start_wpa_supplicant(args->wireless_interface, args->wireless_config, &pid); if (err != VANILLA_SUCCESS || is_interrupted()) { print_info("FAILED TO START WPA SUPPLICANT"); goto die_and_reenable_managed; @@ -355,7 +365,7 @@ int wpa_setup_environment(const char *wireless_interface, const char *wireless_c // Get control interface const size_t buf_len = 1048576; char *buf = malloc(buf_len); - snprintf(buf, buf_len, "%s/%s", wpa_ctrl_interface, wireless_interface); + snprintf(buf, buf_len, "%s/%s", wpa_ctrl_interface, args->wireless_interface); struct wpa_ctrl *ctrl; while (!(ctrl = wpa_ctrl_open(buf))) { if (is_interrupted()) goto die_and_kill; @@ -368,7 +378,8 @@ int wpa_setup_environment(const char *wireless_interface, const char *wireless_c goto die_and_close; } - ret = callback(ctrl, callback_data); + args->ctrl = ctrl; + ret = args->start_routine(args); die_and_detach: wpa_ctrl_detach(ctrl); @@ -383,19 +394,11 @@ die_and_kill: die_and_reenable_managed: if (is_managed) { - print_info("SETTING %s BACK TO MANAGED", wireless_interface); - enable_networkmanager_on_device(wireless_interface); + print_info("SETTING %s BACK TO MANAGED", args->wireless_interface); + enable_networkmanager_on_device(args->wireless_interface); } die: - // Interrupt our stdin thread - signal(SIGINT, SIG_DFL); - signal(SIGTERM, SIG_DFL); - pthread_kill(stdin_thread, SIGINT); - - // Remove our custom sigint signal handler - //uninstall_interrupt_handler(); - return ret; } @@ -512,12 +515,12 @@ int enable_networkmanager_on_device(const char *wireless_interface) return set_networkmanager_on_device(wireless_interface, 1); } -void* do_relay(void *data) +void *do_relay(void *data) { relay_ports *ports = (relay_ports *) data; char buf[2048]; ssize_t read_size; - while (running && client_address.s_addr != 0) { + while (!is_interrupted() && client_address.s_addr != 0) { read_size = recv(ports->from_socket, buf, sizeof(buf), 0); if (read_size <= 0) { continue; @@ -574,7 +577,7 @@ int open_socket(in_port_t port) void *open_relay(void *data) { - in_port_t port = (in_port_t) data; + in_port_t port = (in_port_t) (uintptr_t) data; int ret = -1; // Open an incoming port from the console @@ -590,23 +593,10 @@ void *open_relay(void *data) } // print_info("ENTERING MAIN LOOP"); - pthread_mutex_lock(&running_mutex); - while (running) { - pthread_mutex_unlock(&running_mutex); - - // print_info("CHECKING CLIENT ADDRESS STATUS"); - pthread_mutex_lock(&client_address_mutex); - struct in_addr addr = {0}; - while (client_address.s_addr == 0 && running) { - pthread_cond_wait(&client_address_waitcond, &client_address_mutex); - } - // print_info("READ CLIENT ADDRESS"); - addr = client_address; - pthread_mutex_unlock(&client_address_mutex); - - if (addr.s_addr != 0) { + while (!is_interrupted()) { + if (client_address.s_addr != 0) { print_info("STARTED RELAYS"); - relay_ports console_to_frontend = create_ports(from_console, port, from_frontend, addr.s_addr, port + 200); + relay_ports console_to_frontend = create_ports(from_console, port, from_frontend, client_address.s_addr, port + 200); relay_ports frontend_to_console = create_ports(from_frontend, port + 100, from_console, inet_addr("192.168.1.10"), port - 100); pthread_t a_thread, b_thread; @@ -620,9 +610,7 @@ void *open_relay(void *data) } // print_info("RELAY EXITED"); - pthread_mutex_lock(&running_mutex); } - pthread_mutex_unlock(&running_mutex); ret = 0; @@ -633,20 +621,18 @@ close_console_connection: close(from_console); close: - return (void *) ret; + return THREADRESULT(ret); } void create_all_relays() { pthread_t vid_thread, aud_thread, msg_thread, cmd_thread, hid_thread; - pthread_create(&vid_thread, NULL, open_relay, (void *) PORT_VID); - pthread_create(&aud_thread, NULL, open_relay, (void *) PORT_AUD); - pthread_create(&msg_thread, NULL, open_relay, (void *) PORT_MSG); - pthread_create(&cmd_thread, NULL, open_relay, (void *) PORT_CMD); - pthread_create(&hid_thread, NULL, open_relay, (void *) PORT_HID); - - pprint("READY\n"); + pthread_create(&vid_thread, NULL, open_relay, THREADRESULT(PORT_VID)); + pthread_create(&aud_thread, NULL, open_relay, THREADRESULT(PORT_AUD)); + pthread_create(&msg_thread, NULL, open_relay, THREADRESULT(PORT_MSG)); + pthread_create(&cmd_thread, NULL, open_relay, THREADRESULT(PORT_CMD)); + pthread_create(&hid_thread, NULL, open_relay, THREADRESULT(PORT_HID)); pthread_join(vid_thread, NULL); pthread_join(aud_thread, NULL); @@ -676,120 +662,29 @@ int call_ip(const char **argv) return VANILLA_SUCCESS; } -void *read_client_control(void *data) +void *thread_handler(void *data) { - int skt = open_socket(VANILLA_PIPE_CMD_SERVER_PORT); - uint32_t control_code; + struct sync_args *args = (struct sync_args *) data; - struct sockaddr_in addr = {0}; - socklen_t addr_size = sizeof(addr); + pthread_mutex_lock(&running_mutex); + running = 1; + pthread_mutex_unlock(&running_mutex); - while (running) { - ssize_t r = recvfrom(skt, &control_code, sizeof(control_code), 0, (struct sockaddr *) &addr, &addr_size); - if (r <= 0) { - continue; - } - - control_code = ntohl(control_code); - switch (control_code) { - case VANILLA_PIPE_CC_BIND: - print_info("RECEIVED BIND SIGNAL"); - pthread_mutex_lock(&client_address_mutex); - client_address = addr.sin_addr; - pthread_cond_broadcast(&client_address_waitcond); - pthread_mutex_unlock(&client_address_mutex); - - control_code = htonl(VANILLA_PIPE_CC_BIND_ACK); - sendto(skt, &control_code, sizeof(control_code), 0, (struct sockaddr *) &addr, sizeof(addr)); - break; - case VANILLA_PIPE_CC_UNBIND: - print_info("RECEIVED UNBIND SIGNAL"); - pthread_mutex_lock(&client_address_mutex); - client_address.s_addr = 0; - pthread_cond_broadcast(&client_address_waitcond); - pthread_mutex_unlock(&client_address_mutex); - break; - } - } - return NULL; -} - -int do_connect(struct wpa_ctrl *ctrl, const char *wireless_interface) -{ - while (1) { - while (!wpa_ctrl_pending(ctrl)) { - sleep(2); - print_info("WAITING FOR CONNECTION"); - - if (is_interrupted()) return VANILLA_ERROR; - } - - char buf[1024]; - size_t actual_buf_len = sizeof(buf); - wpa_ctrl_recv(ctrl, buf, &actual_buf_len); - print_info("CONN RECV: %.*s", actual_buf_len, buf); - - if (memcmp(buf, "<3>CTRL-EVENT-CONNECTED", 23) == 0) { - break; - } - - if (is_interrupted()) return VANILLA_ERROR; - } - - print_info("CONNECTED TO CONSOLE"); - - // Use DHCP on interface - pid_t dhclient_pid; - int r = call_dhcp(wireless_interface, &dhclient_pid); - if (r != VANILLA_SUCCESS) { - print_info("FAILED TO RUN DHCP ON %s", wireless_interface); - return r; - } else { - print_info("DHCP ESTABLISHED"); - } - - call_ip((const char *[]){"ip", "route", "del", "default", "via", "192.168.1.1", "dev", wireless_interface, NULL}); - call_ip((const char *[]){"ip", "route", "del", "192.168.1.0/24", "dev", wireless_interface, NULL}); - call_ip((const char *[]){"route", "add", "-host", "192.168.1.10", "dev", wireless_interface, NULL}); - - pthread_mutex_init(&running_mutex, NULL); - pthread_mutex_init(&client_address_mutex, NULL); - pthread_cond_init(&client_address_waitcond, NULL); + void *ret = args->start_routine(data); - pthread_t client_control_thread; - pthread_create(&client_control_thread, NULL, read_client_control, NULL); + free(args); - create_all_relays(); + pthread_mutex_lock(&running_mutex); + running = 0; + pthread_mutex_unlock(&running_mutex); - pthread_join(client_control_thread, NULL); + pthread_mutex_unlock(&action_mutex); - pthread_cond_destroy(&client_address_waitcond); - pthread_mutex_destroy(&client_address_mutex); - pthread_mutex_destroy(&running_mutex); - - int kill_ret = kill(dhclient_pid, SIGTERM); - print_info("KILLING DHCLIENT %i", dhclient_pid); + return ret; } -size_t read_line_from_fd(int pipe, char *output, size_t max_output_size) -{ - size_t i = 0; - while (i < max_output_size && read(pipe, output, 1) > 0) { - int newline = (*output == '\n'); - output++; - i++; - if (newline) { - break; - } - } - *output = 0; - return i; -} - -size_t read_line_from_file(FILE *file, char *output, size_t max_output_size) -{ - return read_line_from_fd(fileno(file), output, max_output_size); -} +char wireless_authenticate_config_filename[1024] = {0}; +char wireless_connect_config_filename[1024] = {0}; size_t get_home_directory(char *buf, size_t buf_size) { @@ -816,9 +711,6 @@ size_t get_max_path_length() return pathconf(".", _PC_PATH_MAX); } -char wireless_authenticate_config_filename[1024] = {0}; -char wireless_connect_config_filename[1024] = {0}; - const char *get_wireless_connect_config_filename() { if (wireless_connect_config_filename[0] == 0) { @@ -837,13 +729,25 @@ const char *get_wireless_authenticate_config_filename() return wireless_authenticate_config_filename; } -struct sync_args { - uint16_t code; -}; +size_t read_line_from_fd(int pipe, char *output, size_t max_output_size) +{ + size_t i = 0; + while (i < max_output_size && read(pipe, output, 1) > 0) { + int newline = (*output == '\n'); + output++; + i++; + if (newline) { + break; + } + } + *output = 0; + return i; +} -struct connect_args { - const char *wireless_interface; -}; +size_t read_line_from_file(FILE *file, char *output, size_t max_output_size) +{ + return read_line_from_fd(fileno(file), output, max_output_size); +} int create_connect_config(const char *input_config, const char *bssid) { @@ -880,8 +784,10 @@ int create_connect_config(const char *input_config, const char *bssid) return VANILLA_SUCCESS; } -int sync_with_console_internal(struct wpa_ctrl *ctrl, uint16_t code) +void *sync_with_console_internal(void *data) { + struct sync_args *args = (struct sync_args *) data; + char buf[16384]; const size_t buf_len = sizeof(buf); @@ -898,7 +804,7 @@ int sync_with_console_internal(struct wpa_ctrl *ctrl, uint16_t code) // print_info("SCANNING"); actual_buf_len = buf_len; - wpa_ctrl_command(ctrl, "SCAN", buf, &actual_buf_len); + wpa_ctrl_command(args->ctrl, "SCAN", buf, &actual_buf_len); if (!memcmp(buf, "FAIL-BUSY", 9)) { //print_info("DEVICE BUSY, RETRYING"); @@ -913,7 +819,7 @@ int sync_with_console_internal(struct wpa_ctrl *ctrl, uint16_t code) //print_info("WAITING FOR SCAN RESULTS"); actual_buf_len = buf_len; - wpa_ctrl_command(ctrl, "SCAN_RESULTS", buf, &actual_buf_len); + wpa_ctrl_command(args->ctrl, "SCAN_RESULTS", buf, &actual_buf_len); print_info("RECEIVED SCAN RESULTS"); const char *line = strtok(buf, "\n"); @@ -928,17 +834,17 @@ int sync_with_console_internal(struct wpa_ctrl *ctrl, uint16_t code) bssid[17] = '\0'; char wps_buf[100]; - snprintf(wps_buf, sizeof(wps_buf), "WPS_PIN %.*s %04d5678", 17, bssid, code); + snprintf(wps_buf, sizeof(wps_buf), "WPS_PIN %.*s %04d5678", 17, bssid, args->code); size_t actual_buf_len = buf_len; - wpa_ctrl_command(ctrl, wps_buf, buf, &actual_buf_len); + wpa_ctrl_command(args->ctrl, wps_buf, buf, &actual_buf_len); static const int max_wait = 20; int wait_count = 0; int cred_received = 0; while (!is_interrupted()) { - while (wait_count < max_wait && !wpa_ctrl_pending(ctrl)) { + while (wait_count < max_wait && !wpa_ctrl_pending(args->ctrl)) { if (is_interrupted()) goto exit_loop; sleep(1); wait_count++; @@ -950,7 +856,7 @@ int sync_with_console_internal(struct wpa_ctrl *ctrl, uint16_t code) } actual_buf_len = buf_len; - wpa_ctrl_recv(ctrl, buf, &actual_buf_len); + wpa_ctrl_recv(args->ctrl, buf, &actual_buf_len); print_info("CRED RECV: %.*s", buf_len, buf); if (!memcmp("<3>WPS-CRED-RECEIVED", buf, 20)) { @@ -964,7 +870,7 @@ int sync_with_console_internal(struct wpa_ctrl *ctrl, uint16_t code) // Tell wpa_supplicant to save config actual_buf_len = buf_len; print_info("SAVING CONFIG", actual_buf_len, buf); - wpa_ctrl_command(ctrl, "SAVE_CONFIG", buf, &actual_buf_len); + wpa_ctrl_command(args->ctrl, "SAVE_CONFIG", buf, &actual_buf_len); // Create connect config which needs a couple more parameters create_connect_config(get_wireless_authenticate_config_filename(), bssid); @@ -977,23 +883,61 @@ int sync_with_console_internal(struct wpa_ctrl *ctrl, uint16_t code) } while (!found_console); exit_loop: - return found_console ? VANILLA_SUCCESS : VANILLA_ERROR; + return THREADRESULT(found_console ? VANILLA_SUCCESS : VANILLA_ERROR); } -int thunk_to_sync(struct wpa_ctrl *ctrl, void *data) +void *do_connect(void *data) { struct sync_args *args = (struct sync_args *) data; - return sync_with_console_internal(ctrl, args->code); + + while (1) { + while (!wpa_ctrl_pending(args->ctrl)) { + sleep(2); + print_info("WAITING FOR CONNECTION"); + + if (is_interrupted()) return THREADRESULT(VANILLA_ERROR); + } + + char buf[1024]; + size_t actual_buf_len = sizeof(buf); + wpa_ctrl_recv(args->ctrl, buf, &actual_buf_len); + print_info("CONN RECV: %.*s", actual_buf_len, buf); + + if (memcmp(buf, "<3>CTRL-EVENT-CONNECTED", 23) == 0) { + break; + } + + if (is_interrupted()) return THREADRESULT(VANILLA_ERROR); + } + + print_info("CONNECTED TO CONSOLE"); + + // Use DHCP on interface + pid_t dhclient_pid; + int r = call_dhcp(args->wireless_interface, &dhclient_pid); + if (r != VANILLA_SUCCESS) { + print_info("FAILED TO RUN DHCP ON %s", args->wireless_interface); + return THREADRESULT(r); + } else { + print_info("DHCP ESTABLISHED"); + } + + call_ip((const char *[]){"ip", "route", "del", "default", "via", "192.168.1.1", "dev", args->wireless_interface, NULL}); + call_ip((const char *[]){"ip", "route", "del", "192.168.1.0/24", "dev", args->wireless_interface, NULL}); + call_ip((const char *[]){"route", "add", "-host", "192.168.1.10", "dev", args->wireless_interface, NULL}); + + create_all_relays(); + + int kill_ret = kill(dhclient_pid, SIGTERM); + print_info("KILLING DHCLIENT %i", dhclient_pid); + + return THREADRESULT(VANILLA_SUCCESS); } -int thunk_to_connect(struct wpa_ctrl *ctrl, void *data) +void *vanilla_sync_with_console(void *data) { - struct connect_args *args = (struct connect_args *) data; - return do_connect(ctrl, args->wireless_interface); -} + struct sync_args *args = (struct sync_args *) data; -int vanilla_sync_with_console(const char *wireless_interface, uint16_t code) -{ const char *wireless_conf_file; FILE *config; @@ -1001,24 +945,125 @@ int vanilla_sync_with_console(const char *wireless_interface, uint16_t code) config = fopen(wireless_conf_file, "w"); if (!config) { print_info("FAILED TO WRITE TEMP CONFIG: %s", wireless_conf_file); - return VANILLA_ERROR; + return THREADRESULT(VANILLA_ERROR); } fprintf(config, "ctrl_interface=%s\nupdate_config=1\n", wpa_ctrl_interface); fclose(config); - struct sync_args args; - args.code = code; + args->start_routine = sync_with_console_internal; + args->wireless_config = wireless_conf_file; - return wpa_setup_environment(wireless_interface, wireless_conf_file, thunk_to_sync, &args); + void *ret = wpa_setup_environment(args); + + pthread_mutex_lock(&sync_mutex); + sync_result_ready = 1; + sync_result = (uint8_t) (uintptr_t) ret; + pthread_mutex_unlock(&sync_mutex); + + return ret; } -int vanilla_connect_to_console(const char *wireless_interface) +void *vanilla_connect_to_console(void *data) { - struct connect_args args; - args.wireless_interface = wireless_interface; + struct sync_args *args = (struct sync_args *) data; + args->start_routine = do_connect; + args->wireless_config = get_wireless_connect_config_filename(); + return wpa_setup_environment(args); +} - return wpa_setup_environment(wireless_interface, get_wireless_connect_config_filename(), thunk_to_connect, &args); +void vanilla_listen(const char *wireless_interface) +{ + int skt = open_socket(VANILLA_PIPE_CMD_SERVER_PORT); + uint32_t control_code; + + struct sockaddr_in addr = {0}; + socklen_t addr_size = sizeof(addr); + + pthread_mutex_init(&running_mutex, NULL); + pthread_mutex_init(&action_mutex, NULL); + pthread_mutex_init(&main_loop_mutex, NULL); + pthread_mutex_init(&sync_mutex, NULL); + + signal(SIGINT, sigint_handler); + signal(SIGTERM, sigint_handler); + + pthread_t stdin_thread; + pthread_create(&stdin_thread, NULL, read_stdin, NULL); + + main_loop = 1; + + pthread_mutex_lock(&main_loop_mutex); + + pprint("READY\n"); + + while (main_loop) { + pthread_mutex_unlock(&main_loop_mutex); + + // If we got a sync result + pthread_mutex_lock(&sync_mutex); + if (sync_result_ready) { + control_code = htonl(VANILLA_PIPE_CC_SYNC_STATUS | (sync_result & 0xFF)); + sendto(skt, &control_code, sizeof(control_code), 0, (struct sockaddr *) &addr, sizeof(addr)); + sync_result_ready = 0; + } + pthread_mutex_unlock(&sync_mutex); + + ssize_t r = recvfrom(skt, &control_code, sizeof(control_code), 0, (struct sockaddr *) &addr, &addr_size); + if (r <= 0) { + goto repeat_loop; + } + + control_code = ntohl(control_code); + + if ((control_code >> 16) == (VANILLA_PIPE_CC_SYNC >> 16) || control_code == VANILLA_PIPE_CC_CONNECT) { + print_info("RECEIVED SYNC/CONNECT SIGNAL"); + + if (pthread_mutex_trylock(&action_mutex) == 0) { + struct sync_args *args = malloc(sizeof(struct sync_args)); + args->wireless_interface = wireless_interface; + args->code = control_code & 0xFFFF; + args->start_routine = (control_code == VANILLA_PIPE_CC_CONNECT) ? vanilla_connect_to_console : vanilla_sync_with_console; + + client_address = addr.sin_addr; + + // Acknowledge + control_code = htonl(VANILLA_PIPE_CC_BIND_ACK); + sendto(skt, &control_code, sizeof(control_code), 0, (struct sockaddr *) &addr, sizeof(addr)); + + pthread_t thread; + int thread_ret; + if (pthread_create(&thread, NULL, thread_handler, args) != 0) { + print_info("FAILED TO CREATE THREAD"); + free(args); + } + } else { + // Busy + control_code = htonl(VANILLA_PIPE_CC_BUSY); + sendto(skt, &control_code, sizeof(control_code), 0, (struct sockaddr *) &addr, sizeof(addr)); + } + } else if (control_code == VANILLA_PIPE_CC_UNBIND) { + print_info("RECEIVED UNBIND SIGNAL"); + pthread_mutex_lock(&running_mutex); + running = 0; + pthread_mutex_unlock(&running_mutex); + } + +repeat_loop: + pthread_mutex_lock(&main_loop_mutex); + } + + pthread_mutex_unlock(&main_loop_mutex); + + // Interrupt our stdin thread + signal(SIGINT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + pthread_kill(stdin_thread, SIGINT); + + pthread_mutex_destroy(&sync_mutex); + pthread_mutex_destroy(&main_loop_mutex); + pthread_mutex_destroy(&action_mutex); + pthread_mutex_destroy(&running_mutex); } int vanilla_has_config() diff --git a/pipe/linux/wpa.h b/pipe/linux/wpa.h index 416b2e7..d63c840 100644 --- a/pipe/linux/wpa.h +++ b/pipe/linux/wpa.h @@ -8,10 +8,6 @@ struct wpa_ctrl; extern const char *wpa_ctrl_interface; -typedef int (*ready_callback_t)(struct wpa_ctrl *, void *); - -int wpa_setup_environment(const char *wireless_interface, const char *wireless_conf_file, ready_callback_t callback, void *callback_data); - void wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd, char *buf, size_t *buf_len); int start_wpa_supplicant(const char *wireless_interface, const char *config_file, pid_t *pid); @@ -21,10 +17,8 @@ int is_networkmanager_managing_device(const char *wireless_interface, int *is_ma int disable_networkmanager_on_device(const char *wireless_interface); int enable_networkmanager_on_device(const char *wireless_interface); -int vanilla_sync_with_console(const char *wireless_interface, uint16_t code); -int vanilla_connect_to_console(const char *wireless_interface); -int vanilla_has_config(); - void pprint(const char *fmt, ...); +void vanilla_listen(const char *wireless_interface); + #endif // VANILLA_WPA_H From 5b3e3ca9533cd29abbc6a3ce8b444557d75f0034 Mon Sep 17 00:00:00 2001 From: UwUnyaa <16294095+UwUnyaa@users.noreply.github.com> Date: Wed, 9 Oct 2024 06:17:01 +0200 Subject: [PATCH 3/6] implement a TV button (#43) --- app/gamepadhandler.cpp | 1 + app/inputconfigdialog.cpp | 3 ++- app/keymap.cpp | 1 + lib/gamepad/input.c | 1 + lib/vanilla.h | 1 + 5 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/gamepadhandler.cpp b/app/gamepadhandler.cpp index 689af76..14b4c43 100644 --- a/app/gamepadhandler.cpp +++ b/app/gamepadhandler.cpp @@ -21,6 +21,7 @@ GamepadHandler::GamepadHandler(QObject *parent) : QObject(parent) g_buttonMap[SDL_CONTROLLER_BUTTON_Y] = VANILLA_BTN_Y; g_buttonMap[SDL_CONTROLLER_BUTTON_BACK] = VANILLA_BTN_MINUS; g_buttonMap[SDL_CONTROLLER_BUTTON_GUIDE] = VANILLA_BTN_HOME; + g_buttonMap[SDL_CONTROLLER_BUTTON_MISC1] = VANILLA_BTN_TV; g_buttonMap[SDL_CONTROLLER_BUTTON_START] = VANILLA_BTN_PLUS; g_buttonMap[SDL_CONTROLLER_BUTTON_LEFTSTICK] = VANILLA_BTN_L3; g_buttonMap[SDL_CONTROLLER_BUTTON_RIGHTSTICK] = VANILLA_BTN_R3; diff --git a/app/inputconfigdialog.cpp b/app/inputconfigdialog.cpp index 2554fd1..567564f 100644 --- a/app/inputconfigdialog.cpp +++ b/app/inputconfigdialog.cpp @@ -30,7 +30,8 @@ InputConfigDialog::InputConfigDialog(QWidget *parent) : QDialog(parent) layout->addWidget(createButton(tr("D-Pad Left"), VANILLA_BTN_LEFT), 12, 0); layout->addWidget(createButton(tr("D-Pad Right"), VANILLA_BTN_RIGHT), 13, 0); - layout->addWidget(createButton(tr("Home"), VANILLA_BTN_HOME), 14, 1, 1, 3); + layout->addWidget(createButton(tr("TV"), VANILLA_BTN_TV), 14, 1, 1, 1); + layout->addWidget(createButton(tr("Home"), VANILLA_BTN_HOME), 14, 2); layout->addWidget(createButton(tr("Right Stick Up"), VANILLA_AXIS_R_UP), 2, 4); layout->addWidget(createButton(tr("Right Stick Down"), VANILLA_AXIS_R_DOWN), 3, 4); diff --git a/app/keymap.cpp b/app/keymap.cpp index 4af6a18..4224740 100644 --- a/app/keymap.cpp +++ b/app/keymap.cpp @@ -22,6 +22,7 @@ KeyMap::KeyMap() ref[Qt::Key_Return] = VANILLA_BTN_PLUS; ref[Qt::Key_Control] = VANILLA_BTN_MINUS; ref[Qt::Key_H] = VANILLA_BTN_HOME; + ref[Qt::Key_Y] = VANILLA_BTN_TV; ref[Qt::Key_W] = VANILLA_AXIS_L_UP; ref[Qt::Key_A] = VANILLA_AXIS_L_LEFT; ref[Qt::Key_S] = VANILLA_AXIS_L_DOWN; diff --git a/lib/gamepad/input.c b/lib/gamepad/input.c index fd01f3e..3273585 100644 --- a/lib/gamepad/input.c +++ b/lib/gamepad/input.c @@ -211,6 +211,7 @@ void send_input(int socket_hid) if (current_buttons[VANILLA_BTN_L3]) button_mask |= 0x80; if (current_buttons[VANILLA_BTN_R3]) button_mask |= 0x40; + if (current_buttons[VANILLA_BTN_TV]) button_mask |= 0x20; ip.extra_buttons = button_mask; diff --git a/lib/vanilla.h b/lib/vanilla.h index 8467c33..86053a3 100644 --- a/lib/vanilla.h +++ b/lib/vanilla.h @@ -29,6 +29,7 @@ enum VanillaGamepadButtons VANILLA_BTN_MINUS, VANILLA_BTN_PLUS, VANILLA_BTN_HOME, + VANILLA_BTN_TV, VANILLA_BTN_L3, VANILLA_BTN_R3, VANILLA_BTN_LEFT, From cb31a2745add5e6cbe4a3a5887c6af7a3acc2e06 Mon Sep 17 00:00:00 2001 From: VoxelTek <53562267+VoxelTek@users.noreply.github.com> Date: Wed, 9 Oct 2024 14:17:39 +1000 Subject: [PATCH 4/6] Minor additions to README.md, to better assist with compilation (#45) * Add FindFFmpeg.cmake, to fix Ubuntu FFmpeg errors * Rename "finders" to "cmake" * Add Qt minimum version message * I am an idiot, fix CMakeLists.txt * Fix unicode, to fix monochrome text-style for sync icons * Fix text-style for backspace and empty * Expand compilation instructions - Add build packages to dependencies list for each OS - Consolidate two commands into "mkdir build && cd build" - Add command for installing Vanilla --- README.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 1c8b430..97818a1 100644 --- a/README.md +++ b/README.md @@ -22,19 +22,19 @@ Vanilla currently requires the following dependencies: - Debian/Ubuntu ``` - # apt install qt6-base-dev qt6-multimedia-dev libavcodec-dev libavutil-dev libavfilter-dev libsdl2-dev libnl-genl-3-dev isc-dhcp-client libssl-dev + # apt install qt6-base-dev qt6-multimedia-dev libavcodec-dev libavutil-dev libavfilter-dev libsdl2-dev libnl-genl-3-dev isc-dhcp-client libssl-dev build-essential cmake ``` - Fedora ``` - # dnf install qt6-qtbase-devel qt6-qtmultimedia-devel libavcodec-free-devel libavutil-free-devel libavfilter-free-devel libnl3-devel SDL2-devel openssl-devel + # dnf install qt6-qtbase-devel qt6-qtmultimedia-devel libavcodec-free-devel libavutil-free-devel libavfilter-free-devel libnl3-devel SDL2-devel openssl-devel make automake gcc gcc-c++ kernel-devel cmake ``` - Arch ``` - # pacman -S qt6 ffmpeg libnl sdl2 dhclient + # pacman -S qt6 ffmpeg libnl sdl2 dhclient base-devel make cmake ``` - Alpine/postmarketOS ``` - # apk add qt6-qtbase-dev qt6-qtmultimedia-dev ffmpeg-dev libnl3-dev sdl2-dev dhclient + # apk add qt6-qtbase-dev qt6-qtmultimedia-dev ffmpeg-dev libnl3-dev sdl2-dev dhclient build-base cmake ``` The build process is otherwise normal for a CMake program: @@ -42,8 +42,13 @@ The build process is otherwise normal for a CMake program: ``` git clone --recursive https://github.com/vanilla-wiiu/vanilla.git cd vanilla -mkdir build -cd build +mkdir build && cd build cmake .. cmake --build . ``` + +Optionally, to install the program: + +``` +sudo cmake --install . +``` From 404c758322122de1e50eb2796864a3b10d52e571 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Wed, 9 Oct 2024 11:42:21 -0700 Subject: [PATCH 5/6] fixed UI issues with new pipe system --- app/backend.cpp | 84 ++++++++++++++++++++++++++++++++++------------ app/backend.h | 75 ++++++++++++++++++++++++----------------- app/mainwindow.cpp | 23 +++---------- app/mainwindow.h | 1 - pipe/linux/main.c | 29 +++++++++------- 5 files changed, 126 insertions(+), 86 deletions(-) diff --git a/app/backend.cpp b/app/backend.cpp index 6536f4a..611af2e 100644 --- a/app/backend.cpp +++ b/app/backend.cpp @@ -37,66 +37,68 @@ Backend::Backend(QObject *parent) : QObject(parent) void Backend::init() { - emit ready(); + initInternal(); } -BackendViaLocalRoot::BackendViaLocalRoot(const QHostAddress &udpServer, QObject *parent) : Backend(parent) +int Backend::initInternal() { - m_serverAddress = udpServer; + emit ready(); + return 0; } -void BackendViaLocalRoot::interrupt() +void Backend::interrupt() { vanilla_stop(); } -void BackendViaLocalRoot::requestIDR() +void Backend::requestIDR() { vanilla_request_idr(); } -void BackendViaLocalRoot::sync(uint16_t code) +void Backend::sync(uint16_t code) { auto watcher = new QFutureWatcher(); - connect(watcher, &QFutureWatcher::finished, this, &BackendViaLocalRoot::syncFutureCompleted); - watcher->setFuture(QtConcurrent::run(vanilla_sync, code, m_serverAddress.toIPv4Address())); + connect(watcher, &QFutureWatcher::finished, this, &Backend::syncFutureCompleted); + watcher->setFuture(QtConcurrent::run(&Backend::syncInternal, this, code)); } -void BackendViaLocalRoot::connectToConsole() +int Backend::syncInternal(uint16_t code) { - QtConcurrent::run(connectInternal, this, m_serverAddress); + return vanilla_sync(code, 0); } -int BackendViaLocalRoot::connectInternal(BackendViaLocalRoot *instance, const QHostAddress &server) +void Backend::connectToConsole() { - if (server.isNull()) { - return vanilla_start(vanillaEventHandler, instance); - } else { - return vanilla_start_udp(vanillaEventHandler, instance, server.toIPv4Address()); - } + QtConcurrent::run(&Backend::connectInternal, this); } -void BackendViaLocalRoot::updateTouch(int x, int y) +int Backend::connectInternal() +{ + return vanilla_start(vanillaEventHandler, this); +} + +void Backend::updateTouch(int x, int y) { vanilla_set_touch(x, y); } -void BackendViaLocalRoot::setButton(int button, int32_t value) +void Backend::setButton(int button, int32_t value) { vanilla_set_button(button, value); } -void BackendViaLocalRoot::setRegion(int region) +void Backend::setRegion(int region) { vanilla_set_region(region); } -void BackendViaLocalRoot::setBatteryStatus(int status) +void Backend::setBatteryStatus(int status) { vanilla_set_battery_status(status); } -void BackendViaLocalRoot::syncFutureCompleted() +void Backend::syncFutureCompleted() { QFutureWatcher *watcher = static_cast*>(sender()); int r = watcher->result(); @@ -135,7 +137,7 @@ void BackendPipe::receivedData() while (m_process->canReadLine()) { QByteArray a = m_process->readLine().trimmed(); if (a == QByteArrayLiteral("READY")) { - // Do nothing? + emit pipeAvailable(); } else { printf("%s\n", a.constData()); } @@ -150,4 +152,42 @@ void BackendPipe::quit() m_process->deleteLater(); m_process = nullptr; } +} + +BackendViaInternalPipe::BackendViaInternalPipe(const QString &wirelessInterface, QObject *parent) : Backend(parent) +{ + m_wirelessInterface = wirelessInterface; +} + +int BackendViaInternalPipe::initInternal() +{ + m_pipe = new BackendPipe(m_wirelessInterface, this); + connect(m_pipe, &BackendPipe::pipeAvailable, this, &BackendViaInternalPipe::ready); + m_pipe->start(); + return 0; +} + +int BackendViaInternalPipe::syncInternal(uint16_t code) +{ + return vanilla_sync(code, QHostAddress(QHostAddress::LocalHost).toIPv4Address()); +} + +int BackendViaInternalPipe::connectInternal() +{ + return vanilla_start_udp(vanillaEventHandler, this, QHostAddress(QHostAddress::LocalHost).toIPv4Address()); +} + +BackendViaExternalPipe::BackendViaExternalPipe(const QHostAddress &udpServer, QObject *parent) : Backend(parent) +{ + m_serverAddress = udpServer; +} + +int BackendViaExternalPipe::syncInternal(uint16_t code) +{ + return vanilla_sync(code, m_serverAddress.toIPv4Address()); +} + +int BackendViaExternalPipe::connectInternal() +{ + return vanilla_start_udp(vanillaEventHandler, this, m_serverAddress.toIPv4Address()); } \ No newline at end of file diff --git a/app/backend.h b/app/backend.h index c640963..7b79e49 100644 --- a/app/backend.h +++ b/app/backend.h @@ -44,12 +44,12 @@ public: Backend(QObject *parent = nullptr); // These are all commands that can be issued to the backend. They are re-entrant and can be called at any time. - virtual void interrupt() = 0; - virtual void updateTouch(int x, int y) = 0; - virtual void setButton(int button, int32_t value) = 0; - virtual void requestIDR() = 0; - virtual void setRegion(int region) = 0; - virtual void setBatteryStatus(int status) = 0; + void interrupt(); + void updateTouch(int x, int y); + void setButton(int button, int32_t value); + void requestIDR(); + void setRegion(int region); + void setBatteryStatus(int status); signals: void videoAvailable(const QByteArray &packet); @@ -63,36 +63,49 @@ signals: public slots: // These slots must be called with Qt::QueuedConnection to start the event loops in the backend's thread - virtual void init(); - virtual void sync(uint16_t code) = 0; - virtual void connectToConsole() = 0; + void init(); + void sync(uint16_t code); + void connectToConsole(); -}; - -class BackendViaLocalRoot : public Backend -{ - Q_OBJECT -public: - BackendViaLocalRoot(const QHostAddress &serverAddress, QObject *parent = nullptr); - - virtual void interrupt() override; - virtual void updateTouch(int x, int y) override; - virtual void setButton(int button, int32_t value) override; - virtual void requestIDR() override; - virtual void setRegion(int region) override; - virtual void setBatteryStatus(int status) override; - -public slots: - virtual void sync(uint16_t code) override; - virtual void connectToConsole() override; - -private: - static int connectInternal(BackendViaLocalRoot *instance, const QHostAddress &serverAddress); - QHostAddress m_serverAddress; +protected: + virtual int initInternal(); + virtual int syncInternal(uint16_t code); + virtual int connectInternal(); private slots: void syncFutureCompleted(); }; +class BackendViaInternalPipe : public Backend +{ + Q_OBJECT +public: + BackendViaInternalPipe(const QString &wirelessInterface, QObject *parent = nullptr); + +protected: + virtual int initInternal() override; + virtual int syncInternal(uint16_t code) override; + virtual int connectInternal() override; + +private: + QString m_wirelessInterface; + BackendPipe *m_pipe; +}; + +class BackendViaExternalPipe : public Backend +{ + Q_OBJECT +public: + BackendViaExternalPipe(const QHostAddress &serverAddress, QObject *parent = nullptr); + +protected: + // virtual int initInternal() override; + virtual int syncInternal(uint16_t code) override; + virtual int connectInternal() override; + +private: + QHostAddress m_serverAddress; +}; + #endif // BACKEND_H \ No newline at end of file diff --git a/app/mainwindow.cpp b/app/mainwindow.cpp index 36a52c6..458fbf8 100644 --- a/app/mainwindow.cpp +++ b/app/mainwindow.cpp @@ -38,7 +38,6 @@ MainWindow::MainWindow(QWidget *parent) : QWidget(parent) } m_backend = nullptr; - m_pipe = nullptr; qRegisterMetaType("uint16_t"); @@ -260,10 +259,6 @@ MainWindow::~MainWindow() delete t; } - if (m_pipe) { - m_pipe->quit(); - } - SDL_Quit(); delete m_viewer; @@ -356,7 +351,7 @@ void MainWindow::initBackend(T func) if (localWirelessIntf.isEmpty()) { UdpAddressDialog udpDiag(this); if (udpDiag.exec() == QDialog::Accepted) { - m_backend = new BackendViaLocalRoot(udpDiag.acceptedAddress()); + m_backend = new BackendViaExternalPipe(udpDiag.acceptedAddress()); } else { d->deleteLater(); closeBackend(); @@ -364,12 +359,9 @@ void MainWindow::initBackend(T func) } // } else if (geteuid() == 0) { // If root, use lib locally - // m_backend = new BackendViaLocalRoot(QHostAddress()); + // m_backend = new Backend(QHostAddress()); } else { - m_pipe = new BackendPipe(localWirelessIntf, this); - m_pipe->start(); - - m_backend = new BackendViaLocalRoot(QHostAddress::LocalHost); + m_backend = new BackendViaInternalPipe(localWirelessIntf); } connect(m_backend, &Backend::closed, d, &BackendInitDialog::deleteLater); @@ -420,16 +412,8 @@ void MainWindow::setConnectedState(bool on) updateBatteryStatus(); }); } else { - if (m_pipe) { - m_pipe->quit(); - m_pipe->deleteLater(); - m_pipe = nullptr; - } - if (m_backend) { m_backend->interrupt(); - m_backend->deleteLater(); - m_backend = nullptr; } m_viewer->setImage(QImage()); @@ -547,6 +531,7 @@ void MainWindow::updateBatteryStatus() void MainWindow::closeBackend() { setConnectedState(false); + if (m_backend) { m_backend->interrupt(); m_backend->deleteLater(); diff --git a/app/mainwindow.h b/app/mainwindow.h index bbe63b2..d813578 100644 --- a/app/mainwindow.h +++ b/app/mainwindow.h @@ -54,7 +54,6 @@ private: QSplitter *m_splitter; Backend *m_backend; - BackendPipe *m_pipe; VideoDecoder *m_videoDecoder; GamepadHandler *m_gamepadHandler; diff --git a/pipe/linux/main.c b/pipe/linux/main.c index ebe550a..badb49b 100644 --- a/pipe/linux/main.c +++ b/pipe/linux/main.c @@ -9,23 +9,26 @@ int main(int argc, const char **argv) { if (argc < 2) { pprint("vanilla-pipe - brokers a connection between Vanilla and the Wii U\n"); + pprint("--------------------------------------------------------------------------------\n"); pprint("\n"); - pprint("Usage: %s [args]\n", argv[0]); + pprint("Usage: %s \n", argv[0]); pprint("\n"); - pprint("Modes: \n"); - pprint(" -sync Sync/authenticate with the Wii U.\n"); - pprint(" -connect Connect to the Wii U (requires syncing prior).\n"); - pprint(" -is_synced Returns 1 if gamepad has been synced or 0 if it hasn't yet.\n"); + pprint("Connecting to the Wii U as a gamepad requires some modifications to the 802.11n\n"); + pprint("protocol, and not all platforms allow such modifications to be made.\n"); + pprint("Additionally, such modifications usually require root level access, and it can\n"); + pprint("be undesirable for various reasons to run a GUI application as root.\n"); pprint("\n"); - pprint("Sync code is a 4-digit PIN based on the card suits shown on the console.\n\n"); - pprint(" To calculate the code, use the following:\n"); + pprint("This necessitated the creation of `vanilla-pipe`, a thin program that can\n"); + pprint("handle connecting to the Wii U in an environment that supports it (e.g. as root,\n"); + pprint("and in a Linux VM, or a separate Linux PC) while forwarding all data to the\n"); + pprint("user's desired frontend. `libvanilla` has full support for integrating with\n"); + pprint("`vanilla-pipe`, so as long as the frontend uses `libvanilla`, it can be used.\n"); + pprint("Additionally, since `vanilla-pipe` is fairly simple, it can be ported to\n"); + pprint("embedded devices such as MCUs or SBCs, providing more versatility in hardware\n"); + pprint("configurations.\n"); pprint("\n"); - pprint(" ♠ (spade) = 0\n"); - pprint(" ♥ (heart) = 1\n"); - pprint(" ♦ (diamond) = 2\n"); - pprint(" ♣ (clover) = 3\n"); - pprint("\n"); - pprint(" Example: ♣♠♥♦ (clover, spade, heart, diamond) would equal 3012\n"); + pprint("`vanilla-pipe` cannot be controlled directly, it can only be controlled via UDP\n"); + pprint("sockets by a compatible frontend.\n"); pprint("\n"); return 1; From 42223f55e981faf27fc4cac4a50e8b95f257489a Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Wed, 9 Oct 2024 11:58:34 -0700 Subject: [PATCH 6/6] lib: resend until pipe acks --- lib/gamepad/gamepad.c | 4 +++- pipe/linux/wpa.c | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/gamepad/gamepad.c b/lib/gamepad/gamepad.c index 173f123..c6397e8 100644 --- a/lib/gamepad/gamepad.c +++ b/lib/gamepad/gamepad.c @@ -97,7 +97,7 @@ int send_pipe_cc(int skt, uint32_t cc, int wait_for_reply) do { sendto(skt, &send_cc, sizeof(send_cc), 0, (struct sockaddr *) &addr, sizeof(addr)); - if (1 || !wait_for_reply) { + if (!wait_for_reply) { return 1; } @@ -105,6 +105,8 @@ int send_pipe_cc(int skt, uint32_t cc, int wait_for_reply) if (read_size == sizeof(recv_cc) && ntohl(recv_cc) == VANILLA_PIPE_CC_BIND_ACK) { return 1; } + + sleep(1); } while (!is_interrupted()); return 0; diff --git a/pipe/linux/wpa.c b/pipe/linux/wpa.c index 095c948..dc818ce 100644 --- a/pipe/linux/wpa.c +++ b/pipe/linux/wpa.c @@ -678,6 +678,7 @@ void *thread_handler(void *data) running = 0; pthread_mutex_unlock(&running_mutex); + // Locked by calling thread pthread_mutex_unlock(&action_mutex); return ret;