shithub: m8c

Download patch

ref: 3d909f5bf38b1c6fb00742f82980c03fb8ecd24f
parent: c3a336812cedf3024dfa1cdf059a0a91649830fc
author: Maido <v3rm0n@users.noreply.github.com>
date: Mon Aug 25 05:32:34 EDT 2025

Fix libusb audio and serial (#195)

* Fix libusb audio and serial

* Fix warnings

--- a/Android.mk
+++ b/Android.mk
@@ -10,7 +10,7 @@
 
 LOCAL_CFLAGS += -DUSE_LIBUSB
 
-LOCAL_SHARED_LIBRARIES := usb-1.0 SDL2
+LOCAL_SHARED_LIBRARIES := usb-1.0 SDL3
 
 LOCAL_LDLIBS := -lGLESv1_CM -lGLESv2 -lOpenSLES -llog -landroid
 
--- a/src/backends/audio_libusb.c
+++ b/src/backends/audio_libusb.c
@@ -1,3 +1,5 @@
+#include "SDL3/SDL_audio.h"
+#include "SDL3/SDL_error.h"
 #ifdef USE_LIBUSB
 
 #include "m8.h"
@@ -15,47 +17,109 @@
 
 extern libusb_device_handle *devh;
 
-SDL_AudioDeviceID sdl_audio_device_id = 0;
+SDL_AudioStream *sdl_audio_stream = NULL;
 int audio_initialized = 0;
 RingBuffer *audio_buffer = NULL;
+static uint8_t *audio_callback_buffer = NULL;
+static size_t audio_callback_buffer_size = 0;
+static int audio_prebuffer_filled = 0;
+#define PREBUFFER_SIZE (8 * 1024)  // Wait for 8KB before starting playback
 
-static void audio_callback(void *userdata, Uint8 *stream, int len) {
-  uint32_t read_len = ring_buffer_pop(audio_buffer, stream, len);
+static void audio_callback(void *userdata, SDL_AudioStream *stream, int additional_amount, int total_amount) {
+  (void)userdata;  // Suppress unused parameter warning
+  (void)additional_amount;  // Suppress unused parameter warning
+  
+  // Reallocate callback buffer if needed
+  if (audio_callback_buffer_size < (size_t)total_amount) {
+    audio_callback_buffer = SDL_realloc(audio_callback_buffer, total_amount);
+    if (audio_callback_buffer == NULL) {
+      SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to allocate audio buffer");
+      return;
+    }
+    audio_callback_buffer_size = (size_t)total_amount;
+  }
 
-  if (read_len == -1) {
-    SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Buffer underflow!");
+  // Try to get audio data from ring buffer
+  uint32_t available_bytes = audio_buffer->size;
+  
+  // Check if we have enough data for initial buffering
+  if (!audio_prebuffer_filled && available_bytes < PREBUFFER_SIZE) {
+    // Not enough data yet, output silence and wait
+    SDL_memset(audio_callback_buffer, 0, total_amount);
+    if(!SDL_PutAudioStreamData(stream, audio_callback_buffer, total_amount)) {
+      SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to put audio stream data: %s", SDL_GetError());
+    }
+    return;
   }
+  
+  // Mark prebuffer as filled once we have enough data
+  if (!audio_prebuffer_filled) {
+    audio_prebuffer_filled = 1;
+    SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Audio prebuffer filled, starting playback");
+  }
 
-  // If we didn't read the full len bytes, fill the rest with zeros
-  if (read_len < len) {
-    SDL_memset(&stream[read_len], 0, len - read_len);
+  if (available_bytes >= (uint32_t)total_amount) {
+    // We have enough data, read it
+    uint32_t read_len = ring_buffer_pop(audio_buffer, audio_callback_buffer, total_amount);
+    if (read_len > 0) {
+      if(!SDL_PutAudioStreamData(stream, audio_callback_buffer, read_len)) {
+        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to put audio stream data: %s", SDL_GetError());
+      }
+    }
+  } else if (available_bytes > 0) {
+    // We have some data but not enough - read what we can and pad with silence
+    uint32_t read_len = ring_buffer_pop(audio_buffer, audio_callback_buffer, available_bytes);
+    SDL_memset(audio_callback_buffer + read_len, 0, total_amount - read_len);
+    if(!SDL_PutAudioStreamData(stream, audio_callback_buffer, total_amount)) {
+      SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to put audio stream data: %s", SDL_GetError());
+    }
+    SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Partial buffer: %d/%d bytes", available_bytes, total_amount);
+  } else {
+    // No data available - put silence and reset prebuffer flag
+    SDL_memset(audio_callback_buffer, 0, total_amount);
+    if(!SDL_PutAudioStreamData(stream, audio_callback_buffer, total_amount)) {
+      SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to put audio stream data: %s", SDL_GetError());
+    }
+    audio_prebuffer_filled = 0;  // Reset prebuffer to avoid continuous dropouts
+    SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Buffer underflow! Resetting prebuffer");
   }
 }
 
 static void cb_xfr(struct libusb_transfer *xfr) {
   unsigned int i;
+  static int error_count = 0;
 
-  for (i = 0; i < xfr->num_iso_packets; i++) {
+  for (i = 0; i < (unsigned int)xfr->num_iso_packets; i++) {
     struct libusb_iso_packet_descriptor *pack = &xfr->iso_packet_desc[i];
 
     if (pack->status != LIBUSB_TRANSFER_COMPLETED) {
-      SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "XFR callback error (status %d: %s)", pack->status,
-                   libusb_error_name(pack->status));
-      /* This doesn't happen, so bail out if it does. */
-      return;
+      error_count++;
+      if (error_count % 100 == 1) { // Log only every 100th error to avoid spam
+        SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "XFR callback error (status %d: %s)", pack->status,
+                     libusb_error_name(pack->status));
+      }
+      continue; // Skip this packet but continue processing others
     }
 
-    const uint8_t *data = libusb_get_iso_packet_buffer_simple(xfr, i);
-    if (sdl_audio_device_id != 0) {
-      uint32_t actual = ring_buffer_push(audio_buffer, data, pack->actual_length);
-      if (actual == -1) {
-        SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Buffer overflow!");
+    if (pack->actual_length > 0) {
+      const uint8_t *data = libusb_get_iso_packet_buffer_simple(xfr, i);
+      if (sdl_audio_stream != 0 && audio_buffer != NULL) {
+        uint32_t actual = ring_buffer_push(audio_buffer, data, pack->actual_length);
+        if (actual == (uint32_t)-1) {
+          SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Buffer overflow!");
+        }
       }
     }
   }
 
-  if (libusb_submit_transfer(xfr) < 0) {
-    SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "error re-submitting URB\n");
+  // Reset error count on successful transfer
+  if (xfr->status == LIBUSB_TRANSFER_COMPLETED) {
+    error_count = 0;
+  }
+
+  int submit_result = libusb_submit_transfer(xfr);
+  if (submit_result < 0) {
+    SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "error re-submitting URB: %s", libusb_error_name(submit_result));
     SDL_free(xfr->buffer);
   }
 }
@@ -84,20 +148,28 @@
   return 1;
 }
 
-int audio_initialize(int audio_buffer_size, const char *output_device_name) {
-  SDL_LogError(SDL_LOG_CATEGORY_AUDIO,"LIBUSB audio not implemented yet");
-  return -1;
-  /*
+int audio_initialize(const char *output_device_name, unsigned int audio_buffer_size) {
+  (void)audio_buffer_size;  // Suppress unused parameter warning
+  
   SDL_Log("USB audio setup");
 
+  if (devh == NULL) {
+    SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Device handle is NULL - cannot initialize audio");
+    return -1;
+  }
+
   int rc;
 
   rc = libusb_kernel_driver_active(devh, IFACE_NUM);
+  if (rc < 0) {
+    SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error checking kernel driver status: %s", libusb_error_name(rc));
+    return rc;
+  }
   if (rc == 1) {
     SDL_Log("Detaching kernel driver");
     rc = libusb_detach_kernel_driver(devh, IFACE_NUM);
     if (rc < 0) {
-      SDL_Log("Could not detach kernel driver: %s\n", libusb_error_name(rc));
+      SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Could not detach kernel driver: %s", libusb_error_name(rc));
       return rc;
     }
   }
@@ -115,7 +187,7 @@
   }
 
   if (!SDL_WasInit(SDL_INIT_AUDIO)) {
-    if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
+    if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) {
       SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Init audio failed %s", SDL_GetError());
       return -1;
     }
@@ -127,29 +199,29 @@
   audio_spec.format = SDL_AUDIO_S16;
   audio_spec.channels = 2;
   audio_spec.freq = 44100;
-  audio_spec.samples = audio_buffer_size;
-  audio_spec.callback = audio_callback;
 
-  SDL_AudioSpec _obtained;
-  SDL_zero(_obtained);
-
   SDL_Log("Current audio driver is %s and device %s", SDL_GetCurrentAudioDriver(),
           output_device_name);
 
+  // Create larger ring buffer for stable audio - about 1.5 seconds at 44.1kHz stereo 16-bit
+  audio_buffer = ring_buffer_create(256 * 1024);
+
   if (SDL_strcasecmp(SDL_GetCurrentAudioDriver(), "openslES") == 0 || output_device_name == NULL) {
     SDL_Log("Using default audio device");
-    sdl_audio_device_id = SDL_OpenAudioDevice(NULL, 0, &audio_spec, &_obtained, 0);
+    sdl_audio_stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &audio_spec, &audio_callback, &audio_buffer);
   } else {
-    sdl_audio_device_id = SDL_OpenAudioDevice(output_device_name, 0, &audio_spec, &_obtained, 0);
+    // TODO: Implement audio device selection
+    sdl_audio_stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &audio_spec, &audio_callback, &audio_buffer);
   }
 
-  audio_buffer = ring_buffer_create(4 * _obtained.size);
+  if (sdl_audio_stream == NULL) {
+    SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Failed to open audio stream: %s", SDL_GetError());
+    ring_buffer_free(audio_buffer);
+    return -1;
+  }
 
-  SDL_Log("Obtained audio spec. Sample rate: %d, channels: %d, samples: %d, size: %d",
-          _obtained.freq, _obtained.channels, _obtained.samples, +_obtained.size);
+  SDL_ResumeAudioStreamDevice(sdl_audio_stream);
 
-  SDL_PauseAudioDevice(sdl_audio_device_id, 0);
-
   // Good to go
   SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Starting capture");
   if ((rc = benchmark_in()) < 0) {
@@ -157,15 +229,22 @@
     return rc;
   }
 
+  audio_initialized = 1;
+  audio_prebuffer_filled = 0;  // Reset prebuffer state
   SDL_Log("Successful init");
   return 1;
-  */
+
 }
 
 void audio_close() {
-  if (devh == NULL || !audio_initialized) {
+  if (devh == NULL) {
+    SDL_LogDebug(SDL_LOG_CATEGORY_AUDIO, "Device handle is NULL - audio already closed or not initialized");
     return;
   }
+  if (!audio_initialized) {
+    SDL_LogDebug(SDL_LOG_CATEGORY_AUDIO, "Audio not initialized - nothing to close");
+    return;
+  }
 
   SDL_LogDebug(SDL_LOG_CATEGORY_AUDIO, "Closing audio");
 
@@ -187,19 +266,30 @@
     return;
   }
 
-  if (sdl_audio_device_id != 0) {
-    SDL_Log("Closing audio device %d", sdl_audio_device_id);
-    const SDL_AudioDeviceID device = sdl_audio_device_id;
-    sdl_audio_device_id = 0;
-    SDL_CloseAudioDevice(device);
+  if (sdl_audio_stream != NULL) {
+    SDL_Log("Closing audio device");
+    SDL_DestroyAudioStream(sdl_audio_stream);
+    sdl_audio_stream = 0;
   }
 
   SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Audio closed");
 
   ring_buffer_free(audio_buffer);
+
+  // Free callback buffer
+  if (audio_callback_buffer) {
+    SDL_free(audio_callback_buffer);
+    audio_callback_buffer = NULL;
+    audio_callback_buffer_size = 0;
+  }
+
+  audio_initialized = 0;
+  audio_prebuffer_filled = 0;
 }
 
-void audio_toggle(unsigned int audio_buffer_size, const char *output_device_name) {
+void audio_toggle(const char *output_device_name, unsigned int audio_buffer_size) {
+  (void)output_device_name;  // Suppress unused parameter warning
+  (void)audio_buffer_size;  // Suppress unused parameter warning
   SDL_Log("Libusb audio toggling not implemented yet");
 }
 
--- a/src/backends/m8_libusb.c
+++ b/src/backends/m8_libusb.c
@@ -3,6 +3,7 @@
 
 // Contains portions of code from libserialport's examples released to the
 // public domain
+#include "m8.h"
 #ifdef USE_LIBUSB
 
 #include <SDL3/SDL.h>
@@ -9,6 +10,9 @@
 #include <libusb.h>
 #include <stdlib.h>
 #include <string.h>
+#include "../command.h"
+#include "queue.h"
+#include "slip.h"
 
 static int ep_out_addr = 0x03;
 static int ep_in_addr = 0x83;
@@ -19,11 +23,24 @@
 #define M8_VID 0x16c0
 #define M8_PID 0x048a
 
+#define SERIAL_READ_SIZE 1024  // maximum amount of bytes to read from the serial in one pass
+
 libusb_context *ctx = NULL;
 libusb_device_handle *devh = NULL;
-
+static uint8_t serial_buffer[SERIAL_READ_SIZE] = {0};
+static uint8_t slip_buffer[SERIAL_READ_SIZE] = {0};
+static slip_handler_s slip;
+message_queue_s queue;
 static int do_exit = 0;
+static int async_transfer_active = 0;
+static struct libusb_transfer *async_transfer = NULL;
+static int shutdown_in_progress = 0;
 
+static int send_message_to_queue(uint8_t *data, const uint32_t size) {
+  push_message(&queue, data, size);
+  return 1;
+}
+
 int m8_list_devices() {
   int r;
   r = libusb_init(&ctx);
@@ -34,7 +51,7 @@
 
   libusb_device **device_list = NULL;
   int count = libusb_get_device_list(ctx, &device_list);
-  for (size_t idx = 0; idx < count; ++idx) {
+  for (size_t idx = 0; idx < (size_t)count; ++idx) {
     libusb_device *device = device_list[idx];
     struct libusb_device_descriptor desc;
     int rc = libusb_get_device_descriptor(device, &desc);
@@ -53,7 +70,9 @@
   return 0;
 }
 
-int usb_loop(void *data) {
+static int usb_loop(void *data) {
+  (void)data;  // Suppress unused parameter warning
+  
   SDL_SetCurrentThreadPriority(SDL_THREAD_PRIORITY_TIME_CRITICAL);
   while (!do_exit) {
     int rc = libusb_handle_events(ctx);
@@ -72,7 +91,7 @@
   *completed = 1;
 }
 
-int bulk_transfer(int endpoint, uint8_t *serial_buf, int count, unsigned int timeout_ms) {
+static int bulk_transfer(int endpoint, uint8_t *serial_buf, int count, unsigned int timeout_ms) {
   if (devh == NULL) {
     return -1;
   }
@@ -113,10 +132,153 @@
   return bulk_transfer(ep_out_addr, buf, count, timeout_ms);
 }
 
-int m8_process_data(uint8_t *serial_buf, int count) {
-  return bulk_transfer(ep_in_addr, serial_buf, count, 1);
+// This function is currently unused but kept for potential future use
+__attribute__((unused))
+static int bulk_async_transfer(int endpoint, uint8_t *serial_buf, int count, unsigned int timeout_ms,
+                        void (*f)(struct libusb_transfer *), void *user_data) {
+  struct libusb_transfer *transfer;
+  transfer = libusb_alloc_transfer(1);
+  libusb_fill_bulk_stream_transfer(transfer, devh, endpoint, 0, serial_buf, count, f, user_data,
+                                   timeout_ms);
+  int r = libusb_submit_transfer(transfer);
+
+  if (r < 0) {
+    SDL_Log("Error");
+    libusb_free_transfer(transfer);
+    return r;
+  }
+  return 0;
 }
 
+static void async_callback(struct libusb_transfer *xfr) {
+  if (shutdown_in_progress) {
+    async_transfer_active = 0;
+    return;
+  }
+
+  if (xfr->status != LIBUSB_TRANSFER_COMPLETED) {
+    if (xfr->status == LIBUSB_TRANSFER_CANCELLED) {
+      SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Async transfer cancelled");
+      async_transfer_active = 0;
+      return;
+    }
+    SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Async transfer failed with status: %s", 
+                 libusb_error_name(xfr->status));
+    if (!shutdown_in_progress && libusb_submit_transfer(xfr) < 0) {
+      SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error re-submitting failed transfer");
+      async_transfer_active = 0;
+    }
+    return;
+  }
+
+  int bytes_read = xfr->actual_length;
+  if (bytes_read < 0) {
+    SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "Error %d reading serial", (int)bytes_read);
+  } else if (bytes_read > 0) {
+    SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Received %d bytes from M8", bytes_read);
+    uint8_t *serial_buf = xfr->buffer;
+    uint8_t *cur = serial_buf;
+    const uint8_t *end = serial_buf + bytes_read;
+    slip_handler_s *slip = (slip_handler_s *)xfr->user_data;
+    while (cur < end) {
+      // process the incoming bytes into commands and draw them
+      int n = slip_read_byte(slip, *(cur++));
+      if (n != SLIP_NO_ERROR) {
+        if (n == SLIP_ERROR_INVALID_PACKET) {
+          SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Invalid SLIP packet!\n");
+
+        } else {
+          SDL_LogError(SDL_LOG_CATEGORY_ERROR, "SLIP error %d\n", n);
+        }
+      }
+    }
+  }
+  if (!shutdown_in_progress) {
+    int submit_result = libusb_submit_transfer(xfr);
+    if (submit_result < 0) {
+      SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error re-submitting URB: %s", 
+                   libusb_error_name(submit_result));
+      async_transfer_active = 0;
+    }
+  } else {
+    async_transfer_active = 0;
+  }
+}
+
+int async_read_start(uint8_t *serial_buf, int count, slip_handler_s *slip) {
+  if (async_transfer_active) {
+    SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Async transfer already active, skipping");
+    return 0; // Already active
+  }
+  
+  if (devh == NULL) {
+    SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Device handle is NULL, cannot start async transfer");
+    return -1;
+  }
+  
+  async_transfer = libusb_alloc_transfer(1);
+  if (!async_transfer) {
+    SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Failed to allocate async transfer");
+    return -1;
+  }
+  
+  libusb_fill_bulk_stream_transfer(async_transfer, devh, ep_in_addr, 0, serial_buf, count, 
+                                   &async_callback, slip, 300);
+  int r = libusb_submit_transfer(async_transfer);
+  
+  if (r < 0) {
+    SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error starting async transfer: %s", libusb_error_name(r));
+    libusb_free_transfer(async_transfer);
+    async_transfer = NULL;
+    return r;
+  }
+  
+  async_transfer_active = 1;
+  SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Async transfer started successfully");
+  return 0;
+}
+
+void async_read_stop() {
+  shutdown_in_progress = 1;
+  
+  if (async_transfer_active && async_transfer) {
+    SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Stopping async transfer");
+    int cancel_result = libusb_cancel_transfer(async_transfer);
+    if (cancel_result < 0) {
+      if (cancel_result == LIBUSB_ERROR_NOT_FOUND) {
+        SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Transfer already completed or cancelled");
+      } else if (cancel_result == LIBUSB_ERROR_INVALID_PARAM) {
+        SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Transfer not valid for cancellation");
+      } else {
+        SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Transfer cancellation returned: %s", 
+                     libusb_error_name(cancel_result));
+      }
+    }
+    // Wait briefly for the callback to complete
+    for (int i = 0; i < 10 && async_transfer_active; i++) {
+      SDL_Delay(1);
+    }
+    // Force cleanup if still active
+    async_transfer_active = 0;
+  }
+}
+
+int m8_process_data(const config_params_s *conf) {
+  (void)conf; // Suppress unused parameter warning
+  // Process any queued messages
+  if (queue_size(&queue) > 0) {
+    unsigned char *command;
+    size_t length = 0;
+    while ((command = pop_message(&queue, &length)) != NULL) {
+      if (length > 0) {
+        process_command(command, length);
+      }
+      SDL_free(command);
+    }
+  }
+  return DEVICE_PROCESSING;
+}
+
 int check_serial_port() {
   // Reading will fail anyway when the device is not present anymore
   return 1;
@@ -166,8 +328,15 @@
     return 0;
   }
 
+  init_queue(&queue);
+
   usb_thread = SDL_CreateThread(&usb_loop, "USB", NULL);
 
+  // Start async transfer for reading data from M8
+  if (async_read_start(serial_buffer, SERIAL_READ_SIZE, &slip) < 0) {
+    SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Failed to start async transfer during initialization");
+  }
+
   return 1;
 }
 
@@ -203,12 +372,22 @@
   return init_interface();
 }
 
-int m8_initialize(int verbose, char *preferred_device) {
+int m8_initialize(int verbose, const char *preferred_device) {
+  (void)verbose; // Suppress unused parameter warning
+  (void)preferred_device; // Suppress unused parameter warning
 
   if (devh != NULL) {
     return 1;
   }
 
+  // Initialize slip descriptor
+  static const slip_descriptor_s slip_descriptor = {
+      .buf = slip_buffer,
+      .buf_size = sizeof(slip_buffer),
+      .recv_message = send_message_to_queue,
+  };
+  slip_init(&slip, &slip_descriptor);
+
   int r;
   r = libusb_init(&ctx);
   if (r < 0) {
@@ -221,11 +400,16 @@
     char *port;
     char *saveptr = NULL;
     char *bus;
-    port = SDL_strtok_r(preferred_device, ":", &saveptr);
+    char *device_copy = SDL_strdup(preferred_device);  // Create a copy to avoid const qualifier warning
+    if (device_copy == NULL) {
+      SDL_Log("Failed to allocate memory for device string");
+      return 0;
+    }
+    port = SDL_strtok_r(device_copy, ":", &saveptr);
     bus = SDL_strtok_r(NULL, ":", &saveptr);
     libusb_device **device_list = NULL;
     int count = libusb_get_device_list(ctx, &device_list);
-    for (size_t idx = 0; idx < count; ++idx) {
+    for (size_t idx = 0; idx < (size_t)count; ++idx) {
       libusb_device *device = device_list[idx];
       struct libusb_device_descriptor desc;
       r = libusb_get_device_descriptor(device, &desc);
@@ -232,6 +416,7 @@
       if (r < 0) {
         SDL_Log("libusb_get_device_descriptor failed: %s", libusb_error_name(r));
         libusb_free_device_list(device_list, 1);
+        SDL_free(device_copy);
         return 0;
       }
 
@@ -243,6 +428,8 @@
           r = libusb_open(device, &devh);
           if (r < 0) {
             SDL_Log("libusb_open failed: %s", libusb_error_name(r));
+            libusb_free_device_list(device_list, 1);
+            SDL_free(device_copy);
             return 0;
           }
         }
@@ -249,6 +436,7 @@
       }
     }
     libusb_free_device_list(device_list, 1);
+    SDL_free(device_copy);  // Free the allocated copy
     if (devh == NULL) {
       SDL_Log("Preferred device %s not found, using first available", preferred_device);
       devh = libusb_open_device_with_vid_pid(ctx, M8_VID, M8_PID);
@@ -280,6 +468,7 @@
 }
 
 int m8_enable_display(const unsigned char reset_display) {
+  (void)reset_display; // Suppress unused parameter warning
   if (devh == NULL) {
     return 0;
   }
@@ -296,8 +485,7 @@
   }
 
   SDL_Delay(5);
-  if ()
-    result = m8_reset_display();
+  result = m8_reset_display();
   return result;
 }
 
@@ -308,6 +496,9 @@
 
   SDL_Log("Disconnecting M8\n");
 
+  // Stop async transfer first
+  async_read_stop();
+
   result = blocking_write(buf, 1, 5);
   if (result != 1) {
     SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error sending disconnect, code %d", result);
@@ -334,6 +525,15 @@
   SDL_WaitThread(usb_thread, NULL);
 
   libusb_exit(ctx);
+
+  destroy_queue(&queue);
+  
+  if (async_transfer) {
+    libusb_free_transfer(async_transfer);
+    async_transfer = NULL;
+  }
+  async_transfer_active = 0;
+  shutdown_in_progress = 0;
 
   return 1;
 }
--- a/src/input.c
+++ b/src/input.c
@@ -366,6 +366,7 @@
 
 // Touch screen double tap: switch between integer scaling / full screen scaling
 void input_handle_finger_down(struct app_context *ctx, const SDL_Event *event) {
+  (void)event; // Suppress unused parameter warning
   static Uint64 last_finger_down_time = 0;
   SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, "Finger down");
   const Uint64 current_time = SDL_GetTicks();
--