shithub: m8c

Download patch

ref: f5b5e38690b2ab4c6851a3848efac64e6da5be5c
parent: 1f73f4f2a5855366cd64856cad764f984ebf956b
parent: 6158be32c845812db304c6add83416ba757722f0
author: Jonne Kokkonen <jonne.kokkonen@gmail.com>
date: Sat Jan 28 10:51:14 EST 2023

Merge pull request #92 from v3rm0n/serial-refactor

Add additional libusb support, refactor serial port usage

--- /dev/null
+++ b/.editorconfig
@@ -1,0 +1,18 @@
+# EditorConfig is awesome: https://EditorConfig.org
+
+# top-most EditorConfig file
+root = true
+
+# Unix-style newlines with a newline ending every file
+[*]
+end_of_line = lf
+insert_final_newline = true
+
+# 2 space indentation
+[*.c]
+indent_style = space
+indent_size = 2
+
+# Tab indentation (no size specified)
+[Makefile]
+indent_style = tab
\ No newline at end of file
--- /dev/null
+++ b/Android.mk
@@ -1,0 +1,17 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := m8c
+
+LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.c)
+
+LOCAL_EXPORT_C_INCLUDES := $(wildcard $(LOCAL_PATH)/*.h)
+
+LOCAL_CFLAGS += -DUSE_LIBUSB
+
+LOCAL_SHARED_LIBRARIES := usb-1.0 SDL2
+
+LOCAL_LDLIBS := -lGLESv1_CM -lGLESv2 -lOpenSLES -llog -landroid
+
+include $(BUILD_SHARED_LIBRARY)
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,8 @@
 #Set all your object files (the object files of all the .c files in your project, e.g. main.o my_sub_functions.o )
-OBJ = main.o serial.o slip.o command.o write.o render.o ini.o config.o input.o font.o fx_cube.o
+OBJ = main.o serial.o slip.o command.o render.o ini.o config.o input.o font.o fx_cube.o usb.o
 
 #Set any dependant header files so that if they are edited they cause a complete re-compile (e.g. main.h some_subfunctions.h some_definitions_file.h ), or leave blank
-DEPS = serial.h slip.h command.h write.h render.h ini.h config.h input.h fx_cube.h
+DEPS = serial.h slip.h command.h render.h ini.h config.h input.h fx_cube.h
 
 #Any special libraries you are using in your project (e.g. -lbcm2835 -lrt `pkg-config --libs gtk+-3.0` ), or leave blank
 INCLUDES = $(shell pkg-config --libs sdl2 libserialport)
@@ -26,6 +26,10 @@
 #Set your desired exe output file name here
 m8c: $(OBJ)
 	$(CC) -o $@ $^ $(local_CFLAGS) $(INCLUDES)
+
+libusb: INCLUDES = $(shell pkg-config --libs sdl2 libusb-1.0)
+libusb: local_CFLAGS = $(CFLAGS) $(shell pkg-config --cflags sdl2 libusb-1.0) -Wall -O2 -pipe -I. -DUSE_LIBUSB=1
+libusb: m8c
 
 font.c: inline_font.h
 	@echo "#include <SDL.h>" > $@-tmp1
--- a/inprint2.c
+++ b/inprint2.c
@@ -2,7 +2,7 @@
 // Released into public domain.
 // Modified to support adding a background to text.
 
-#include <SDL2/SDL.h>
+#include <SDL.h>
 
 #include "inline_font.h" /* Actual font data */
 
--- a/input.c
+++ b/input.c
@@ -7,7 +7,6 @@
 #include "config.h"
 #include "input.h"
 #include "render.h"
-#include "write.h"
 
 #define MAX_CONTROLLERS 4
 
@@ -365,12 +364,12 @@
 
   // Read special case game controller buttons quit and reset
   for (int gc = 0; gc < num_joysticks; gc++) {
-    if (SDL_GameControllerGetButton(game_controllers[gc], conf->gamepad_quit) && 
-        (SDL_GameControllerGetButton(game_controllers[gc], conf->gamepad_select) || 
+    if (SDL_GameControllerGetButton(game_controllers[gc], conf->gamepad_quit) &&
+        (SDL_GameControllerGetButton(game_controllers[gc], conf->gamepad_select) ||
         SDL_GameControllerGetAxis(game_controllers[gc], conf->gamepad_analog_axis_select)))
       key = (input_msg_s){special, msg_quit};
-    else if (SDL_GameControllerGetButton(game_controllers[gc], conf->gamepad_reset) && 
-            (SDL_GameControllerGetButton(game_controllers[gc], conf->gamepad_select) || 
+    else if (SDL_GameControllerGetButton(game_controllers[gc], conf->gamepad_reset) &&
+            (SDL_GameControllerGetButton(game_controllers[gc], conf->gamepad_select) ||
               SDL_GameControllerGetAxis(game_controllers[gc], conf->gamepad_analog_axis_select)))
       key = (input_msg_s){special, msg_reset_display};
   }
@@ -394,7 +393,7 @@
     if (event.window.event == SDL_WINDOWEVENT_RESIZED)
     {
       SDL_Log("Resizing window...");
-      key = (input_msg_s){special, msg_reset_display};      
+      key = (input_msg_s){special, msg_reset_display};
     }
     break;
 
--- a/main.c
+++ b/main.c
@@ -6,7 +6,6 @@
 // #define DEBUG_MSG
 
 #include <SDL.h>
-#include <libserialport.h>
 #include <signal.h>
 
 #include "command.h"
@@ -15,11 +14,7 @@
 #include "render.h"
 #include "serial.h"
 #include "slip.h"
-#include "write.h"
 
-// maximum amount of bytes to read from the serial in one read()
-#define serial_read_size 324
-
 enum state { QUIT, WAIT_FOR_DEVICE, RUN };
 
 enum state run = WAIT_FOR_DEVICE;
@@ -28,10 +23,8 @@
 // Handles CTRL+C / SIGINT
 void intHandler(int dummy) { run = QUIT; }
 
-void close_serial_port(struct sp_port *port) {
-  disconnect(port);
-  sp_close(port);
-  sp_free_port(port);
+void close_serial_port() {
+  disconnect();
 }
 
 int main(int argc, char *argv[]) {
@@ -56,7 +49,6 @@
   };
 
   static slip_handler_s slip;
-  struct sp_port *port = NULL;
 
   uint8_t prev_input = 0;
   uint8_t prev_note = 0;
@@ -64,13 +56,14 @@
 
   signal(SIGINT, intHandler);
   signal(SIGTERM, intHandler);
-
+#ifdef SIGQUIT
+  signal(SIGQUIT, intHandler);
+#endif
   slip_init(&slip, &slip_descriptor);
 
   // First device detection to avoid SDL init if it isn't necessary
   if (conf.wait_for_device == 0) {
-    port = init_serial(1);
-    if (port == NULL) {
+    if (!init_serial(1)) {
       free(serial_buf);
       return -1;
     }
@@ -89,11 +82,10 @@
 
   // main loop begin
   do {
-    if (port == NULL)
-      port = init_serial(1);
-    if (port != NULL) {
+    int port_inited = init_serial(1);
+    if (port_inited) {
       int result;
-      result = enable_and_reset_display(port);
+      result = enable_and_reset_display();
       if (result == 1) {
         run = RUN;
       } else {
@@ -108,7 +100,7 @@
       static uint32_t ticks_poll_device = 0;
       static uint32_t ticks_update_screen = 0;
 
-      if (port == NULL)
+      if (!port_inited)
         screensaver_init();
 
       while (run == WAIT_FOR_DEVICE) {
@@ -126,11 +118,10 @@
         }
 
         // Poll for M8 device every second
-        if (!port && (SDL_GetTicks() - ticks_poll_device > 1000)) {
+        if (!port_inited && (SDL_GetTicks() - ticks_poll_device > 1000)) {
           ticks_poll_device = SDL_GetTicks();
-          port = init_serial(0);
-          if (run == WAIT_FOR_DEVICE && port != NULL) {
-            int result = enable_and_reset_display(port);
+          if (run == WAIT_FOR_DEVICE && init_serial(0)) {
+            int result = enable_and_reset_display();
             SDL_Delay(100);
             // Device was found; enable display and proceed to the main loop
             if (result == 1) {
@@ -149,7 +140,7 @@
 
     } else {
       // classic startup behaviour, exit if device is not found
-      if (port == NULL) {
+      if (!port_inited) {
         close_game_controllers();
         close_renderer();
         SDL_Quit();
@@ -168,16 +159,16 @@
       case normal:
         if (input.value != prev_input) {
           prev_input = input.value;
-          send_msg_controller(port, input.value);
+          send_msg_controller(input.value);
         }
         break;
       case keyjazz:
         if (input.value != 0) {
           if (input.eventType == SDL_KEYDOWN && input.value != prev_input) {
-            send_msg_keyjazz(port, input.value, input.value2);
+            send_msg_keyjazz(input.value, input.value2);
             prev_note = input.value;
           } else if (input.eventType == SDL_KEYUP && input.value == prev_note) {
-            send_msg_keyjazz(port, 0xFF, 0);
+            send_msg_keyjazz(0xFF, 0);
           }
         }
         prev_input = input.value;
@@ -191,7 +182,7 @@
             run = 0;
             break;
           case msg_reset_display:
-            reset_display(port);
+            reset_display();
             break;
           default:
             break;
@@ -203,7 +194,7 @@
       while (1) {
         // read serial port
         int bytes_read =
-            sp_nonblocking_read(port, serial_buf, serial_read_size);
+            serial_read(serial_buf, serial_read_size);
         if (bytes_read < 0) {
           SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "Error %d reading serial. \n",
                           (int)bytes_read);
@@ -220,7 +211,7 @@
             int n = slip_read_byte(&slip, *(cur++));
             if (n != SLIP_NO_ERROR) {
               if (n == SLIP_ERROR_INVALID_PACKET) {
-                reset_display(port);
+                reset_display();
               } else {
                 SDL_LogError(SDL_LOG_CATEGORY_ERROR, "SLIP error %d\n", n);
               }
@@ -233,13 +224,12 @@
             zerobyte_packets = 0;
 
             // try opening the serial port to check if it's alive
-            if (check_serial_port(port)) {
+            if (check_serial_port()) {
               // the device is still there, carry on
               break;
             } else {
               run = WAIT_FOR_DEVICE;
-              close_serial_port(port);
-              port = NULL;
+              close_serial_port();
               /* we'll make one more loop to see if the device is still there
                * but just sending zero bytes. if it doesn't get detected when
                * resetting the port, it will disconnect */
@@ -258,7 +248,7 @@
   SDL_Log("Shutting down\n");
   close_game_controllers();
   close_renderer();
-  close_serial_port(port);
+  close_serial_port();
   free(serial_buf);
   SDL_Quit();
   return 0;
--- a/serial.c
+++ b/serial.c
@@ -4,25 +4,29 @@
 // Contains portions of code from libserialport's examples released to the
 // public domain
 
-#include <SDL_log.h>
+#ifndef USE_LIBUSB
 #include <libserialport.h>
+#include <SDL.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 
 #include "serial.h"
 
+struct sp_port *m8_port = NULL;
+
 // Helper function for error handling
 static int check(enum sp_return result);
 
-static int detect_m8_serial_device(struct sp_port *port) {
+static int detect_m8_serial_device(struct sp_port *m8_port) {
   // Check the connection method - we want USB serial devices
-  enum sp_transport transport = sp_get_port_transport(port);
+  enum sp_transport transport = sp_get_port_transport(m8_port);
 
   if (transport == SP_TRANSPORT_USB) {
     // Get the USB vendor and product IDs.
     int usb_vid, usb_pid;
-    sp_get_port_usb_vid_pid(port, &usb_vid, &usb_pid);
+    sp_get_port_usb_vid_pid(m8_port, &usb_vid, &usb_pid);
 
     if (usb_vid == 0x16C0 && usb_pid == 0x048A)
       return 1;
@@ -32,7 +36,7 @@
 }
 
 // Checks for connected devices and whether the specified device still exists
-int check_serial_port(struct sp_port *m8_port) {
+int check_serial_port() {
 
   int device_found = 0;
 
@@ -65,10 +69,12 @@
 
 }
 
-struct sp_port *init_serial(int verbose) {
+int init_serial(int verbose) {
+  if(m8_port != NULL) {
+    return 1;
+  }
   /* A pointer to a null-terminated array of pointers to
    * struct sp_port, which will contain the ports found.*/
-  struct sp_port *m8_port = NULL;
   struct sp_port **port_list;
 
   if (verbose)
@@ -103,33 +109,33 @@
 
     result = sp_open(m8_port, SP_MODE_READ_WRITE);
     if (check(result) != SP_OK)
-      return NULL;
+      return 0;
 
     result = sp_set_baudrate(m8_port, 115200);
     if (check(result) != SP_OK)
-      return NULL;
+      return 0;
 
     result = sp_set_bits(m8_port, 8);
     if (check(result) != SP_OK)
-      return NULL;
+      return 0;
 
     result = sp_set_parity(m8_port, SP_PARITY_NONE);
     if (check(result) != SP_OK)
-      return NULL;
+      return 0;
 
     result = sp_set_stopbits(m8_port, 1);
     if (check(result) != SP_OK)
-      return NULL;
+      return 0;
 
     result = sp_set_flowcontrol(m8_port, SP_FLOWCONTROL_NONE);
     if (check(result) != SP_OK)
-      return NULL;
+      return 0;
   } else {
     if (verbose)
       SDL_LogCritical(SDL_LOG_CATEGORY_SYSTEM, "Cannot find a M8.\n");
   }
 
-  return (m8_port);
+  return 1;
 }
 
 // Helper function for error handling.
@@ -160,3 +166,96 @@
   }
   return result;
 }
+
+int reset_display() {
+  SDL_Log("Reset display\n");
+  uint8_t buf[2];
+  int result;
+
+  buf[0] = 0x45;
+  buf[1] = 0x52;
+
+  result = sp_blocking_write(m8_port, buf, 2, 5);
+  if (result != 2) {
+    SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error resetting M8 display, code %d",
+                 result);
+    return 0;
+  }
+  return 1;
+}
+
+int enable_and_reset_display() {
+  uint8_t buf[1];
+  int result;
+
+  SDL_Log("Enabling and resetting M8 display\n");
+
+  buf[0] = 0x44;
+  result = sp_blocking_write(m8_port, buf, 1, 5);
+  if (result != 1) {
+    SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error enabling M8 display, code %d",
+                 result);
+    return 0;
+  }
+
+  SDL_Delay(5);
+  result = reset_display();
+  if (result == 1)
+    return 1;
+  else
+    return 0;
+}
+
+int disconnect() {
+  char buf[1] = {'D'};
+  int result;
+
+  SDL_Log("Disconnecting M8\n");
+
+  result = sp_blocking_write(m8_port, buf, 1, 5);
+  if (result != 1) {
+    SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error sending disconnect, code %d",
+                 result);
+    return -1;
+  }
+  sp_close(m8_port);
+  sp_free_port(m8_port);
+  m8_port = NULL;
+  return 1;
+}
+
+int serial_read(uint8_t *serial_buf, int count) {
+  return sp_nonblocking_read(m8_port, serial_buf, count);
+}
+
+int send_msg_controller(uint8_t input) {
+  char buf[2] = {'C', input};
+  size_t nbytes = 2;
+  int result;
+  result = sp_blocking_write(m8_port, buf, nbytes, 5);
+  if (result != nbytes) {
+    SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error sending input, code %d",
+                 result);
+    return -1;
+  }
+  return 1;
+}
+
+int send_msg_keyjazz(uint8_t note, uint8_t velocity) {
+  if (velocity > 0x7F)
+    velocity = 0x7F;
+  char buf[3] = {'K', note, velocity};
+  size_t nbytes = 3;
+  int result;
+  result = sp_blocking_write(m8_port, buf, nbytes, 5);
+  if (result != nbytes) {
+    SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error sending keyjazz, code %d",
+                 result);
+    return -1;
+  }
+
+  return 1;
+}
+#endif
+
+
--- a/serial.h
+++ b/serial.h
@@ -4,9 +4,26 @@
 #ifndef _SERIAL_H_
 #define _SERIAL_H_
 
-#include <libserialport.h>
+#ifdef USE_LIBUSB
+// Not sure about this value but it seems to work
+#define serial_read_size 512
+#include <libusb.h>
+typedef int (*usb_callback_t)(libusb_device_handle *devh);
+void set_usb_init_callback(usb_callback_t callback);
+void set_usb_destroy_callback(usb_callback_t callback);
+int init_serial_with_file_descriptor(int file_descriptor);
+#else
+// maximum amount of bytes to read from the serial in one read()
+#define serial_read_size 324
+#endif
 
-struct sp_port *init_serial(int verbose);
-int check_serial_port(struct sp_port *m8_port);
+int init_serial(int verbose);
+int check_serial_port();
+int reset_display();
+int enable_and_reset_display();
+int disconnect();
+int serial_read(uint8_t *serial_buf, int count);
+int send_msg_controller(uint8_t input);
+int send_msg_keyjazz(uint8_t note, uint8_t velocity);
 
-#endif
\ No newline at end of file
+#endif
--- /dev/null
+++ b/usb.c
@@ -1,0 +1,297 @@
+// Copyright 2021 Jonne Kokkonen
+// Released under the MIT licence, https://opensource.org/licenses/MIT
+
+// Contains portions of code from libserialport's examples released to the
+// public domain
+#ifdef USE_LIBUSB
+
+#include <SDL.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <libusb.h>
+
+#include "serial.h"
+
+
+static int ep_out_addr = 0x03;
+static int ep_in_addr = 0x83;
+
+#define ACM_CTRL_DTR   0x01
+#define ACM_CTRL_RTS   0x02
+
+usb_callback_t init_callback = NULL;
+usb_callback_t destroy_callback = NULL;
+libusb_device_handle *devh = NULL;
+
+void set_usb_init_callback(usb_callback_t callback) {
+    init_callback = callback;
+}
+
+void set_usb_destroy_callback(usb_callback_t callback) {
+    destroy_callback = callback;
+}
+
+int blocking_write(void *buf,
+                   int count, unsigned int timeout_ms) {
+    int actual_length;
+    int rc;
+    rc = libusb_bulk_transfer(devh, ep_out_addr, buf, count,
+                         &actual_length, timeout_ms);
+    if (rc < 0) {
+        SDL_Log("Error while sending char: %s", libusb_error_name(rc));
+        return -1;
+    }
+    return actual_length;
+}
+
+int serial_read(uint8_t *serial_buf, int count) {
+    int actual_length;
+    int rc = libusb_bulk_transfer(devh, ep_in_addr, serial_buf, count, &actual_length,
+                                  10);
+    if (rc == LIBUSB_ERROR_TIMEOUT) {
+        return 0;
+    } else if (rc < 0) {
+        SDL_Log("Error while waiting for char: %s", libusb_error_name(rc));
+        return -1;
+    }
+
+    return actual_length;
+}
+
+int check_serial_port() {
+    // Reading will fail anyway when the device is not present anymore
+    return 1;
+}
+
+int init_interface() {
+
+    if (devh == NULL) {
+        SDL_Log("Device not initialised!");
+        return 0;
+    }
+
+    int rc;
+
+    for (int if_num = 0; if_num < 2; if_num++) {
+        if (libusb_kernel_driver_active(devh, if_num)) {
+            SDL_Log("Detaching kernel driver for interface %d", if_num);
+            libusb_detach_kernel_driver(devh, if_num);
+        }
+        rc = libusb_claim_interface(devh, if_num);
+        if (rc < 0) {
+            SDL_Log("Error claiming interface: %s", libusb_error_name(rc));
+            return 0;
+        }
+    }
+
+    /* Start configuring the device:
+     * - set line state
+     */
+    SDL_Log("Setting line state");
+    rc = libusb_control_transfer(devh, 0x21, 0x22, ACM_CTRL_DTR | ACM_CTRL_RTS,
+                                 0, NULL, 0, 0);
+    if (rc < 0) {
+        SDL_Log("Error during control transfer: %s", libusb_error_name(rc));
+        return 0;
+    }
+
+    /* - set line encoding: here 115200 8N1
+     * 115200 = 0x01C200 ~> 0x00, 0xC2, 0x01, 0x00 in little endian
+     */
+    SDL_Log("Set line encoding");
+    unsigned char encoding[] = {0x00, 0xC2, 0x01, 0x00, 0x00, 0x00, 0x08};
+    rc = libusb_control_transfer(devh, 0x21, 0x20, 0, 0, encoding,
+                                 sizeof(encoding), 0);
+    if (rc < 0) {
+        SDL_Log("Error during control transfer: %s", libusb_error_name(rc));
+        return 0;
+    }
+
+    return 1;
+}
+
+int init_serial_with_file_descriptor(int file_descriptor) {
+    SDL_Log("Initialising serial with file descriptor");
+
+    if (file_descriptor <= 0) {
+        SDL_Log("Invalid file descriptor: %d", file_descriptor);
+        return 0;
+    }
+
+    int r;
+    r = libusb_set_option(NULL, LIBUSB_OPTION_NO_DEVICE_DISCOVERY, NULL);
+    if (r != LIBUSB_SUCCESS) {
+        SDL_Log("libusb_set_option failed: %s", libusb_error_name(r));
+        return 0;
+    }
+    r = libusb_init(NULL);
+    if (r < 0) {
+        SDL_Log("libusb_init failed: %s", libusb_error_name(r));
+        return 0;
+    }
+    r = libusb_wrap_sys_device(NULL, (intptr_t) file_descriptor, &devh);
+    if (r < 0) {
+        SDL_Log("libusb_wrap_sys_device failed: %s", libusb_error_name(r));
+        return 0;
+    } else if (devh == NULL) {
+        SDL_Log("libusb_wrap_sys_device returned invalid handle");
+        return 0;
+    }
+    SDL_Log("USB device init success");
+
+    if (init_callback != NULL) {
+        r = init_callback(devh);
+
+        if (r < 0) {
+            SDL_Log("Init callback failed: %d", r);
+            return 0;
+        }
+    }
+
+    return init_interface();
+}
+
+int init_serial(int verbose) {
+
+    if (devh != NULL) {
+        return 1;
+    }
+
+    int r;
+    r = libusb_init(NULL);
+    if (r < 0) {
+        SDL_Log("libusb_init failed: %s", libusb_error_name(r));
+        return 0;
+    }
+    devh = libusb_open_device_with_vid_pid(NULL, 0x16c0, 0x048a);
+    if (devh == NULL) {
+        SDL_Log("libusb_open_device_with_vid_pid returned invalid handle");
+        return 0;
+    }
+    SDL_Log("USB device init success");
+
+    if (init_callback != NULL) {
+        r = init_callback(devh);
+
+        if (r < 0) {
+            SDL_Log("Init callback failed: %d", r);
+            return 0;
+        }
+    }
+
+    return init_interface();
+}
+
+int reset_display() {
+    SDL_Log("Reset display\n");
+    uint8_t buf[2];
+    int result;
+
+    buf[0] = 0x45;
+    buf[1] = 0x52;
+
+    result = blocking_write(buf, 2, 5);
+    if (result != 2) {
+        SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error resetting M8 display, code %d",
+                     result);
+        return 0;
+    }
+    return 1;
+}
+
+int enable_and_reset_display() {
+    uint8_t buf[1];
+    int result;
+
+    SDL_Log("Enabling and resetting M8 display\n");
+
+    buf[0] = 0x44;
+    result = blocking_write(buf, 1, 5);
+    if (result != 1) {
+        SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error enabling M8 display, code %d",
+                     result);
+        return 0;
+    }
+
+    SDL_Delay(5);
+    result = reset_display();
+    if (result == 1)
+        return 1;
+    else
+        return 0;
+}
+
+int disconnect() {
+
+    char buf[1] = {'D'};
+    int result;
+
+    if (destroy_callback != NULL) {
+        result = destroy_callback(devh);
+
+        if (result < 0) {
+            SDL_Log("Destroy callback failed: %d", result);
+            return result;
+        }
+    }
+
+    SDL_Log("Disconnecting M8\n");
+
+    result = blocking_write(buf, 1, 5);
+    if (result != 1) {
+        SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error sending disconnect, code %d",
+                     result);
+        return -1;
+    }
+
+    int rc;
+
+    for (int if_num = 0; if_num < 2; if_num++) {
+        rc = libusb_release_interface(devh, if_num);
+        if (rc < 0) {
+            SDL_Log("Error releasing interface: %s", libusb_error_name(rc));
+            return 0;
+        }
+    }
+
+    if (devh != NULL) {
+        libusb_close(devh);
+    }
+
+    libusb_exit(NULL);
+
+    return 1;
+}
+
+int send_msg_controller(uint8_t input) {
+    char buf[2] = {'C', input};
+    int nbytes = 2;
+    int result;
+    result = blocking_write(buf, nbytes, 5);
+    if (result != nbytes) {
+        SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error sending input, code %d",
+                     result);
+        return -1;
+    }
+    return 1;
+}
+
+int send_msg_keyjazz(uint8_t note, uint8_t velocity) {
+    if (velocity > 0x7F)
+        velocity = 0x7F;
+    char buf[3] = {'K', note, velocity};
+    int nbytes = 3;
+    int result;
+    result = blocking_write(buf, nbytes, 5);
+    if (result != nbytes) {
+        SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error sending keyjazz, code %d",
+                     result);
+        return -1;
+    }
+
+    return 1;
+}
+
+#endif
\ No newline at end of file
--- a/write.c
+++ /dev/null
@@ -1,92 +1,0 @@
-// Copyright 2021 Jonne Kokkonen
-// Released under the MIT licence, https://opensource.org/licenses/MIT
-
-#include "SDL_timer.h"
-#include <SDL_log.h>
-#include <libserialport.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <unistd.h>
-
-int reset_display(struct sp_port *port) {
-  SDL_Log("Reset display\n");
-  uint8_t buf[2];
-  int result;
-
-  buf[0] = 0x45;
-  buf[1] = 0x52;
-
-  result = sp_blocking_write(port, buf, 2, 5);
-  if (result != 2) {
-    SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error resetting M8 display, code %d",
-                 result);
-    return 0;
-  }
-  return 1;
-}
-
-int enable_and_reset_display(struct sp_port *port) {
-  uint8_t buf[1];
-  int result;
-
-  SDL_Log("Enabling and resetting M8 display\n");
-
-  buf[0] = 0x44;
-  result = sp_blocking_write(port, buf, 1, 5);
-  if (result != 1) {
-    SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error enabling M8 display, code %d",
-                 result);
-    return 0;
-  }
-
-  SDL_Delay(5);
-  result = reset_display(port);
-  if (result == 1)
-    return 1;
-  else
-    return 0;
-}
-
-int disconnect(struct sp_port *port) {
-  char buf[1] = {'D'};
-  int result;
-
-  SDL_Log("Disconnecting M8\n");
-
-  result = sp_blocking_write(port, buf, 1, 5);
-  if (result != 1) {
-    SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error sending disconnect, code %d",
-                 result);
-    return -1;
-  }
-  return 1;
-}
-
-int send_msg_controller(struct sp_port *port, uint8_t input) {
-  char buf[2] = {'C', input};
-  size_t nbytes = 2;
-  int result;
-  result = sp_blocking_write(port, buf, nbytes, 5);
-  if (result != nbytes) {
-    SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error sending input, code %d",
-                 result);
-    return -1;
-  }
-  return 1;
-}
-
-int send_msg_keyjazz(struct sp_port *port, uint8_t note, uint8_t velocity) {
-  if (velocity > 0x7F)
-    velocity = 0x7F;
-  char buf[3] = {'K', note, velocity};
-  size_t nbytes = 3;
-  int result;
-  result = sp_blocking_write(port, buf, nbytes, 5);
-  if (result != nbytes) {
-    SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "Error sending keyjazz, code %d",
-                 result);
-    return -1;
-  }
-
-  return 1;
-}
--- a/write.h
+++ /dev/null
@@ -1,16 +1,0 @@
-// Copyright 2021 Jonne Kokkonen
-// Released under the MIT licence, https://opensource.org/licenses/MIT
-
-#ifndef WRITE_H_
-#define WRITE_H_
-
-#include <stdint.h>
-#include <libserialport.h>
-
-int reset_display(struct sp_port *port);
-int enable_and_reset_display(struct sp_port *port);
-int disconnect(struct sp_port *port);
-int send_msg_controller(struct sp_port *port, uint8_t input);
-int send_msg_keyjazz(struct sp_port *port, uint8_t note, uint8_t velocity);
-
-#endif
\ No newline at end of file
--