]> git.donarmstrong.com Git - qmk_firmware.git/blobdiff - tmk_core/protocol/lufa/lufa.c
Initial version of Raw HID interface
[qmk_firmware.git] / tmk_core / protocol / lufa / lufa.c
index 65c215bf850f2b09e7141a150e3fd10874a963bb..aeb5f07815378f1da7f37c0f38080cabad30621d 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
  * Copyright 2012 Jun Wako <wakojun@gmail.com>
  * This file is based on:
  *     LUFA-120219/Demos/Device/Lowlevel/KeyboardMouse
 
 #include "descriptor.h"
 #include "lufa.h"
+#include "quantum.h"
+
+#ifdef NKRO_ENABLE
+  #include "keycode_config.h"
+
+  extern keymap_config_t keymap_config;
+#endif
+
+
+#ifdef AUDIO_ENABLE
+    #include <audio.h>
+#endif
+
+#ifdef BLUETOOTH_ENABLE
+    #include "bluetooth.h"
+#endif
+
+#ifdef VIRTSER_ENABLE
+    #include "virtser.h"
+#endif
+
+#if (defined(RGB_MIDI) | defined(RGBLIGHT_ANIMATIONS)) & defined(RGBLIGHT_ENABLE)
+    #include "rgblight.h"        
+#endif
+
+#ifdef MIDI_ENABLE
+  #include "sysex_tools.h"
+#endif
+
+#ifdef RAW_ENABLE
+       #include "raw_hid.h"
+#endif
 
 uint8_t keyboard_idle = 0;
+/* 0: Boot Protocol, 1: Report Protocol(default) */
 uint8_t keyboard_protocol = 1;
 static uint8_t keyboard_led_stats = 0;
 
 static report_keyboard_t keyboard_report_sent;
 
+#ifdef MIDI_ENABLE
+static void usb_send_func(MidiDevice * device, uint16_t cnt, uint8_t byte0, uint8_t byte1, uint8_t byte2);
+static void usb_get_midi(MidiDevice * device);
+static void midi_usb_init(MidiDevice * device);
+#endif
 
 /* Host driver */
 static uint8_t keyboard_leds(void);
@@ -70,9 +108,151 @@ host_driver_t lufa_driver = {
     send_keyboard,
     send_mouse,
     send_system,
-    send_consumer
+    send_consumer,
+#ifdef MIDI_ENABLE
+    usb_send_func,
+    usb_get_midi,
+    midi_usb_init
+#endif
 };
 
+/*******************************************************************************
+ * MIDI
+ ******************************************************************************/
+
+#ifdef MIDI_ENABLE
+USB_ClassInfo_MIDI_Device_t USB_MIDI_Interface =
+{
+  .Config =
+  {
+    .StreamingInterfaceNumber = AS_INTERFACE,
+    .DataINEndpoint           =
+    {
+      .Address          = MIDI_STREAM_IN_EPADDR,
+      .Size             = MIDI_STREAM_EPSIZE,
+      .Banks            = 1,
+    },
+    .DataOUTEndpoint          =
+    {
+      .Address          = MIDI_STREAM_OUT_EPADDR,
+      .Size             = MIDI_STREAM_EPSIZE,
+      .Banks            = 1,
+    },
+  },
+};
+
+#define SYSEX_START_OR_CONT 0x40
+#define SYSEX_ENDS_IN_1 0x50
+#define SYSEX_ENDS_IN_2 0x60
+#define SYSEX_ENDS_IN_3 0x70
+
+#define SYS_COMMON_1 0x50
+#define SYS_COMMON_2 0x20
+#define SYS_COMMON_3 0x30
+#endif
+
+#ifdef VIRTSER_ENABLE
+USB_ClassInfo_CDC_Device_t cdc_device =
+{
+  .Config =
+  {
+    .ControlInterfaceNumber = CCI_INTERFACE,
+    .DataINEndpoint         =
+    {
+      .Address         = CDC_IN_EPADDR,
+      .Size            = CDC_EPSIZE,
+      .Banks           = 1,
+    },
+    .DataOUTEndpoint       =
+    {
+      .Address         = CDC_OUT_EPADDR,
+      .Size            = CDC_EPSIZE,
+      .Banks           = 1,
+    },
+    .NotificationEndpoint   =
+    {
+      .Address         = CDC_NOTIFICATION_EPADDR,
+      .Size            = CDC_NOTIFICATION_EPSIZE,
+      .Banks           = 1,
+    },
+  },
+};
+#endif
+
+#ifdef RAW_ENABLE
+
+void raw_hid_send( uint8_t *data, uint8_t length )
+{
+       // TODO: implement variable size packet
+       if ( length != RAW_EPSIZE )
+       {
+               return;
+       }
+
+       if (USB_DeviceState != DEVICE_STATE_Configured)
+       {
+               return;
+       }
+
+       // TODO: decide if we allow calls to raw_hid_send() in the middle
+       // of other endpoint usage.
+       uint8_t ep = Endpoint_GetCurrentEndpoint();
+
+       Endpoint_SelectEndpoint(RAW_IN_EPNUM);
+
+       // Check to see if the host is ready to accept another packet
+       if (Endpoint_IsINReady())
+       {
+               // Write data
+               Endpoint_Write_Stream_LE(data, RAW_EPSIZE, NULL);
+               // Finalize the stream transfer to send the last packet
+               Endpoint_ClearIN();
+       }
+
+       Endpoint_SelectEndpoint(ep);
+}
+
+__attribute__ ((weak))
+void raw_hid_receive( uint8_t *data, uint8_t length )
+{
+       // Users should #include "raw_hid.h" in their own code
+       // and implement this function there. Leave this as weak linkage
+       // so users can opt to not handle data coming in.
+}
+
+static void raw_hid_task(void)
+{
+       // Create a temporary buffer to hold the read in data from the host
+       uint8_t data[RAW_EPSIZE];
+       bool data_read = false;
+
+       // Device must be connected and configured for the task to run
+       if (USB_DeviceState != DEVICE_STATE_Configured)
+       return;
+
+       Endpoint_SelectEndpoint(RAW_OUT_EPNUM);
+
+       // Check to see if a packet has been sent from the host
+       if (Endpoint_IsOUTReceived())
+       {
+               // Check to see if the packet contains data
+               if (Endpoint_IsReadWriteAllowed())
+               {
+                       /* Read data */
+                       Endpoint_Read_Stream_LE(data, sizeof(data), NULL);
+                       data_read = true;
+               }
+
+               // Finalize the stream transfer to receive the last packet
+               Endpoint_ClearOUT();
+
+               if ( data_read )
+               {
+                       raw_hid_receive( data, sizeof(data) );
+               }
+       }
+}
+#endif
 
 /*******************************************************************************
  * Console
@@ -98,10 +278,10 @@ static void Console_Task(void)
         {
             /* Create a temporary buffer to hold the read in report from the host */
             uint8_t ConsoleData[CONSOLE_EPSIZE];
+
             /* Read Console Report Data */
             Endpoint_Read_Stream_LE(&ConsoleData, sizeof(ConsoleData), NULL);
+
             /* Process Console Report Data */
             //ProcessConsoleHIDReport(ConsoleData);
         }
@@ -129,10 +309,6 @@ static void Console_Task(void)
 
     Endpoint_SelectEndpoint(ep);
 }
-#else
-static void Console_Task(void)
-{
-}
 #endif
 
 
@@ -162,7 +338,7 @@ void EVENT_USB_Device_Disconnect(void)
     print("[D]");
     /* For battery powered device */
     USB_IsInitialized = false;
-/* TODO: This doesn't work. After several plug in/outs can not be enumerated. 
+/* TODO: This doesn't work. After several plug in/outs can not be enumerated.
     if (USB_IsInitialized) {
         USB_Disable();  // Disable all interrupts
        USB_Controller_Enable();
@@ -196,6 +372,8 @@ void EVENT_USB_Device_WakeUp()
 #endif
 }
 
+
+
 #ifdef CONSOLE_ENABLE
 static bool console_flush = false;
 #define CONSOLE_FLUSH_SET(b)   do { \
@@ -213,10 +391,14 @@ void EVENT_USB_Device_StartOfFrame(void)
     Console_Task();
     console_flush = false;
 }
+
 #endif
 
 /** Event handler for the USB_ConfigurationChanged event.
  * This is fired when the host sets the current configuration of the USB device after enumeration.
+ *
+ * ATMega32u2 supports dual bank(ping-pong mode) only on endpoint 3 and 4,
+ * it is safe to use singl bank for all endpoints.
  */
 void EVENT_USB_Device_ConfigurationChanged(void)
 {
@@ -238,10 +420,18 @@ void EVENT_USB_Device_ConfigurationChanged(void)
                                      EXTRAKEY_EPSIZE, ENDPOINT_BANK_SINGLE);
 #endif
 
+#ifdef RAW_ENABLE
+    /* Setup Raw HID Report Endpoints */
+    ConfigSuccess &= ENDPOINT_CONFIG(RAW_IN_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_IN,
+                                                                        RAW_EPSIZE, ENDPOINT_BANK_SINGLE);
+    ConfigSuccess &= ENDPOINT_CONFIG(RAW_OUT_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_OUT,
+                                                                        RAW_EPSIZE, ENDPOINT_BANK_SINGLE);
+#endif
+
 #ifdef CONSOLE_ENABLE
     /* Setup Console HID Report Endpoints */
     ConfigSuccess &= ENDPOINT_CONFIG(CONSOLE_IN_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_IN,
-                                     CONSOLE_EPSIZE, ENDPOINT_BANK_DOUBLE);
+                                     CONSOLE_EPSIZE, ENDPOINT_BANK_SINGLE);
 #if 0
     ConfigSuccess &= ENDPOINT_CONFIG(CONSOLE_OUT_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_OUT,
                                      CONSOLE_EPSIZE, ENDPOINT_BANK_SINGLE);
@@ -253,6 +443,17 @@ void EVENT_USB_Device_ConfigurationChanged(void)
     ConfigSuccess &= ENDPOINT_CONFIG(NKRO_IN_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_IN,
                                      NKRO_EPSIZE, ENDPOINT_BANK_SINGLE);
 #endif
+
+#ifdef MIDI_ENABLE
+    ConfigSuccess &= Endpoint_ConfigureEndpoint(MIDI_STREAM_IN_EPADDR, EP_TYPE_BULK, MIDI_STREAM_EPSIZE, ENDPOINT_BANK_SINGLE);
+    ConfigSuccess &= Endpoint_ConfigureEndpoint(MIDI_STREAM_OUT_EPADDR, EP_TYPE_BULK, MIDI_STREAM_EPSIZE, ENDPOINT_BANK_SINGLE);
+#endif
+
+#ifdef VIRTSER_ENABLE
+    ConfigSuccess &= Endpoint_ConfigureEndpoint(CDC_NOTIFICATION_EPADDR, EP_TYPE_INTERRUPT, CDC_NOTIFICATION_EPSIZE, ENDPOINT_BANK_SINGLE);
+    ConfigSuccess &= Endpoint_ConfigureEndpoint(CDC_OUT_EPADDR, EP_TYPE_BULK, CDC_EPSIZE, ENDPOINT_BANK_SINGLE);
+    ConfigSuccess &= Endpoint_ConfigureEndpoint(CDC_IN_EPADDR, EP_TYPE_BULK, CDC_EPSIZE, ENDPOINT_BANK_SINGLE);
+#endif
 }
 
 /*
@@ -346,10 +547,7 @@ void EVENT_USB_Device_ControlRequest(void)
                     Endpoint_ClearSETUP();
                     Endpoint_ClearStatusStage();
 
-                    keyboard_protocol = ((USB_ControlRequest.wValue & 0xFF) != 0x00);
-#ifdef NKRO_ENABLE
-                    keyboard_nkro = !!keyboard_protocol;
-#endif
+                    keyboard_protocol = (USB_ControlRequest.wValue & 0xFF);
                     clear_keyboard();
                 }
             }
@@ -377,10 +575,15 @@ void EVENT_USB_Device_ControlRequest(void)
 
             break;
     }
+
+#ifdef VIRTSER_ENABLE
+    CDC_Device_ProcessControlRequest(&cdc_device);
+#endif
 }
 
 /*******************************************************************************
- * Host driver 
+ * Host driver
+p
  ******************************************************************************/
 static uint8_t keyboard_leds(void)
 {
@@ -389,6 +592,14 @@ static uint8_t keyboard_leds(void)
 
 static void send_keyboard(report_keyboard_t *report)
 {
+
+#ifdef BLUETOOTH_ENABLE
+    bluefruit_serial_send(0xFD);
+    for (uint8_t i = 0; i < KEYBOARD_EPSIZE; i++) {
+        bluefruit_serial_send(report->raw[i]);
+    }
+#endif
+
     uint8_t timeout = 255;
 
     if (USB_DeviceState != DEVICE_STATE_Configured)
@@ -396,7 +607,7 @@ static void send_keyboard(report_keyboard_t *report)
 
     /* Select the Keyboard Report Endpoint */
 #ifdef NKRO_ENABLE
-    if (keyboard_nkro) {
+    if (keyboard_protocol && keymap_config.nkro) {
         /* Report protocol - NKRO */
         Endpoint_SelectEndpoint(NKRO_IN_EPNUM);
 
@@ -430,6 +641,19 @@ static void send_keyboard(report_keyboard_t *report)
 static void send_mouse(report_mouse_t *report)
 {
 #ifdef MOUSE_ENABLE
+
+#ifdef BLUETOOTH_ENABLE
+    bluefruit_serial_send(0xFD);
+    bluefruit_serial_send(0x00);
+    bluefruit_serial_send(0x03);
+    bluefruit_serial_send(report->buttons);
+    bluefruit_serial_send(report->x);
+    bluefruit_serial_send(report->y);
+    bluefruit_serial_send(report->v); // should try sending the wheel v here
+    bluefruit_serial_send(report->h); // should try sending the wheel h here
+    bluefruit_serial_send(0x00);
+#endif
+
     uint8_t timeout = 255;
 
     if (USB_DeviceState != DEVICE_STATE_Configured)
@@ -473,6 +697,23 @@ static void send_system(uint16_t data)
 
 static void send_consumer(uint16_t data)
 {
+
+#ifdef BLUETOOTH_ENABLE
+    static uint16_t last_data = 0;
+    if (data == last_data) return;
+    last_data = data;
+    uint16_t bitmap = CONSUMER2BLUEFRUIT(data);
+    bluefruit_serial_send(0xFD);
+    bluefruit_serial_send(0x00);
+    bluefruit_serial_send(0x02);
+    bluefruit_serial_send((bitmap>>8)&0xFF);
+    bluefruit_serial_send(bitmap&0xFF);
+    bluefruit_serial_send(0x00);
+    bluefruit_serial_send(0x00);
+    bluefruit_serial_send(0x00);
+    bluefruit_serial_send(0x00);
+#endif
+
     uint8_t timeout = 255;
 
     if (USB_DeviceState != DEVICE_STATE_Configured)
@@ -561,6 +802,234 @@ int8_t sendchar(uint8_t c)
 }
 #endif
 
+/*******************************************************************************
+ * MIDI
+ ******************************************************************************/
+
+#ifdef MIDI_ENABLE
+static void usb_send_func(MidiDevice * device, uint16_t cnt, uint8_t byte0, uint8_t byte1, uint8_t byte2) {
+  MIDI_EventPacket_t event;
+  event.Data1 = byte0;
+  event.Data2 = byte1;
+  event.Data3 = byte2;
+
+  uint8_t cable = 0;
+
+// Endpoint_SelectEndpoint(MIDI_STREAM_IN_EPNUM);
+
+  //if the length is undefined we assume it is a SYSEX message
+  if (midi_packet_length(byte0) == UNDEFINED) {
+    switch(cnt) {
+      case 3:
+        if (byte2 == SYSEX_END)
+          event.Event = MIDI_EVENT(cable, SYSEX_ENDS_IN_3);
+        else
+          event.Event = MIDI_EVENT(cable, SYSEX_START_OR_CONT);
+        break;
+      case 2:
+        if (byte1 == SYSEX_END)
+          event.Event = MIDI_EVENT(cable, SYSEX_ENDS_IN_2);
+        else
+          event.Event = MIDI_EVENT(cable, SYSEX_START_OR_CONT);
+        break;
+      case 1:
+        if (byte0 == SYSEX_END)
+          event.Event = MIDI_EVENT(cable, SYSEX_ENDS_IN_1);
+        else
+          event.Event = MIDI_EVENT(cable, SYSEX_START_OR_CONT);
+        break;
+      default:
+        return; //invalid cnt
+    }
+  } else {
+    //deal with 'system common' messages
+    //TODO are there any more?
+    switch(byte0 & 0xF0){
+      case MIDI_SONGPOSITION:
+        event.Event = MIDI_EVENT(cable, SYS_COMMON_3);
+        break;
+      case MIDI_SONGSELECT:
+      case MIDI_TC_QUARTERFRAME:
+        event.Event = MIDI_EVENT(cable, SYS_COMMON_2);
+        break;
+      default:
+        event.Event = MIDI_EVENT(cable, byte0);
+        break;
+    }
+  }
+
+// Endpoint_Write_Stream_LE(&event, sizeof(event), NULL);
+// Endpoint_ClearIN();
+
+  MIDI_Device_SendEventPacket(&USB_MIDI_Interface, &event);
+  MIDI_Device_Flush(&USB_MIDI_Interface);
+  MIDI_Device_USBTask(&USB_MIDI_Interface);
+  USB_USBTask();
+}
+
+static void usb_get_midi(MidiDevice * device) {
+  MIDI_EventPacket_t event;
+  while (MIDI_Device_ReceiveEventPacket(&USB_MIDI_Interface, &event)) {
+
+    midi_packet_length_t length = midi_packet_length(event.Data1);
+    uint8_t input[3];
+    input[0] = event.Data1;
+    input[1] = event.Data2;
+    input[2] = event.Data3;
+    if (length == UNDEFINED) {
+      //sysex
+      if (event.Event == MIDI_EVENT(0, SYSEX_START_OR_CONT) || event.Event == MIDI_EVENT(0, SYSEX_ENDS_IN_3)) {
+        length = 3;
+      } else if (event.Event == MIDI_EVENT(0, SYSEX_ENDS_IN_2)) {
+        length = 2;
+      } else if(event.Event ==  MIDI_EVENT(0, SYSEX_ENDS_IN_1)) {
+        length = 1;
+      } else {
+        //XXX what to do?
+      }
+    }
+
+    //pass the data to the device input function
+    if (length != UNDEFINED)
+      midi_device_input(device, length, input);
+  }
+  MIDI_Device_USBTask(&USB_MIDI_Interface);
+  USB_USBTask();
+}
+
+static void midi_usb_init(MidiDevice * device){
+  midi_device_init(device);
+  midi_device_set_send_func(device, usb_send_func);
+  midi_device_set_pre_input_process_func(device, usb_get_midi);
+
+  // SetupHardware();
+  sei();
+}
+
+void MIDI_Task(void)
+{
+
+    /* Device must be connected and configured for the task to run */
+    dprint("in MIDI_TASK\n");
+    if (USB_DeviceState != DEVICE_STATE_Configured)
+      return;
+    dprint("continuing in MIDI_TASK\n");
+
+    Endpoint_SelectEndpoint(MIDI_STREAM_IN_EPADDR);
+
+    if (Endpoint_IsINReady())
+    {
+
+        dprint("Endpoint is ready\n");
+
+        uint8_t MIDICommand = 0;
+        uint8_t MIDIPitch;
+
+        /* Get board button status - if pressed use channel 10 (percussion), otherwise use channel 1 */
+        uint8_t Channel = MIDI_CHANNEL(1);
+
+        MIDICommand = MIDI_COMMAND_NOTE_ON;
+        MIDIPitch   = 0x3E;
+
+        /* Check if a MIDI command is to be sent */
+        if (MIDICommand)
+        {
+            dprint("Command exists\n");
+            MIDI_EventPacket_t MIDIEvent = (MIDI_EventPacket_t)
+                {
+                    .Event       = MIDI_EVENT(0, MIDICommand),
+
+                    .Data1       = MIDICommand | Channel,
+                    .Data2       = MIDIPitch,
+                    .Data3       = MIDI_STANDARD_VELOCITY,
+                };
+
+            /* Write the MIDI event packet to the endpoint */
+            Endpoint_Write_Stream_LE(&MIDIEvent, sizeof(MIDIEvent), NULL);
+
+            /* Send the data in the endpoint to the host */
+            Endpoint_ClearIN();
+        }
+    }
+
+
+    /* Select the MIDI OUT stream */
+    Endpoint_SelectEndpoint(MIDI_STREAM_OUT_EPADDR);
+
+    /* Check if a MIDI command has been received */
+    if (Endpoint_IsOUTReceived())
+    {
+        MIDI_EventPacket_t MIDIEvent;
+
+        /* Read the MIDI event packet from the endpoint */
+        Endpoint_Read_Stream_LE(&MIDIEvent, sizeof(MIDIEvent), NULL);
+
+        /* If the endpoint is now empty, clear the bank */
+        if (!(Endpoint_BytesInEndpoint()))
+        {
+            /* Clear the endpoint ready for new packet */
+            Endpoint_ClearOUT();
+        }
+    }
+}
+
+#endif
+
+/*******************************************************************************
+ * VIRTUAL SERIAL
+ ******************************************************************************/
+
+#ifdef VIRTSER_ENABLE
+void virtser_init(void)
+{
+  cdc_device.State.ControlLineStates.DeviceToHost = CDC_CONTROL_LINE_IN_DSR ;
+  CDC_Device_SendControlLineStateChange(&cdc_device);
+}
+
+void virtser_recv(uint8_t c) __attribute__ ((weak));
+void virtser_recv(uint8_t c)
+{
+  // Ignore by default
+}
+
+void virtser_task(void)
+{
+  uint16_t count = CDC_Device_BytesReceived(&cdc_device);
+  uint8_t ch;
+  if (count)
+  {
+    ch = CDC_Device_ReceiveByte(&cdc_device);
+    virtser_recv(ch);
+  }
+}
+void virtser_send(const uint8_t byte)
+{
+  uint8_t timeout = 255;
+  uint8_t ep = Endpoint_GetCurrentEndpoint();
+
+  if (cdc_device.State.ControlLineStates.HostToDevice & CDC_CONTROL_LINE_OUT_DTR)
+  {
+    /* IN packet */
+    Endpoint_SelectEndpoint(cdc_device.Config.DataINEndpoint.Address);
+
+    if (!Endpoint_IsEnabled() || !Endpoint_IsConfigured()) {
+        Endpoint_SelectEndpoint(ep);
+        return;
+    }
+
+    while (timeout-- && !Endpoint_IsReadWriteAllowed()) _delay_us(40);
+
+    Endpoint_Write_8(byte);
+    CDC_Device_Flush(&cdc_device);
+
+    if (Endpoint_IsINReady()) {
+      Endpoint_ClearIN();
+    }
+
+    Endpoint_SelectEndpoint(ep);
+  }
+}
+#endif
 
 /*******************************************************************************
  * main
@@ -572,7 +1041,10 @@ static void setup_mcu(void)
     wdt_disable();
 
     /* Disable clock division */
-    clock_prescale_set(clock_div_1);
+    // clock_prescale_set(clock_div_1);
+
+    CLKPR = (1 << CLKPCE);
+    CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0);
 }
 
 static void setup_usb(void)
@@ -587,24 +1059,61 @@ static void setup_usb(void)
     print_set_sendchar(sendchar);
 }
 
+
+#ifdef MIDI_ENABLE
+void fallthrough_callback(MidiDevice * device,
+    uint16_t cnt, uint8_t byte0, uint8_t byte1, uint8_t byte2);
+void cc_callback(MidiDevice * device,
+    uint8_t chan, uint8_t num, uint8_t val);
+void sysex_callback(MidiDevice * device,
+    uint16_t start, uint8_t length, uint8_t * data);
+#endif
+
 int main(void)  __attribute__ ((weak));
 int main(void)
 {
+
+#ifdef MIDI_ENABLE
+    midi_device_init(&midi_device);
+    midi_device_set_send_func(&midi_device, usb_send_func);
+    midi_device_set_pre_input_process_func(&midi_device, usb_get_midi);
+#endif
+
     setup_mcu();
     keyboard_setup();
     setup_usb();
     sei();
 
+#ifdef MIDI_ENABLE
+    midi_register_fallthrough_callback(&midi_device, fallthrough_callback);
+    midi_register_cc_callback(&midi_device, cc_callback);
+    midi_register_sysex_callback(&midi_device, sysex_callback);
+
+    // init_notes();
+    // midi_send_cc(&midi_device, 0, 1, 2);
+    // midi_send_cc(&midi_device, 15, 1, 0);
+    // midi_send_noteon(&midi_device, 0, 64, 127);
+    // midi_send_noteoff(&midi_device, 0, 64, 127);
+#endif
+
+#ifdef BLUETOOTH_ENABLE
+    serial_init();
+#endif
+
     /* wait for USB startup & debug output */
+
+#ifdef WAIT_FOR_USB
     while (USB_DeviceState != DEVICE_STATE_Configured) {
-#if defined(INTERRUPT_CONTROL_ENDPOINT)
-        ;
-#else
-        USB_USBTask();
-#endif
+    #if defined(INTERRUPT_CONTROL_ENDPOINT)
+            ;
+    #else
+            USB_USBTask();
+    #endif
     }
     print("USB configured.\n");
-
+#else
+    USB_USBTask();
+#endif
     /* init modules */
     keyboard_init();
     host_set_driver(&lufa_driver);
@@ -612,8 +1121,13 @@ int main(void)
     sleep_led_init();
 #endif
 
+#ifdef VIRTSER_ENABLE
+    virtser_init();
+#endif
+
     print("Keyboard start.\n");
     while (1) {
+        #ifndef BLUETOOTH_ENABLE
         while (USB_DeviceState == DEVICE_STATE_Suspended) {
             print("[s]");
             suspend_power_down();
@@ -621,11 +1135,88 @@ int main(void)
                     USB_Device_SendRemoteWakeup();
             }
         }
+        #endif
 
         keyboard_task();
 
+#ifdef MIDI_ENABLE
+        midi_device_process(&midi_device);
+        // MIDI_Task();
+#endif
+        
+#if defined(RGBLIGHT_ANIMATIONS) & defined(RGBLIGHT_ENABLE)
+        rgblight_task();
+#endif
+
+#ifdef VIRTSER_ENABLE
+        virtser_task();
+        CDC_Device_USBTask(&cdc_device);
+#endif
+
+#ifdef RAW_ENABLE
+        raw_hid_task();
+#endif
+
 #if !defined(INTERRUPT_CONTROL_ENDPOINT)
         USB_USBTask();
 #endif
+
     }
 }
+
+#ifdef MIDI_ENABLE
+void fallthrough_callback(MidiDevice * device,
+    uint16_t cnt, uint8_t byte0, uint8_t byte1, uint8_t byte2){
+
+#ifdef AUDIO_ENABLE
+  if (cnt == 3) {
+    switch (byte0 & 0xF0) {
+        case MIDI_NOTEON:
+            play_note(((double)261.6)*pow(2.0, -4.0)*pow(2.0,(byte1 & 0x7F)/12.0), (byte2 & 0x7F) / 8);
+            break;
+        case MIDI_NOTEOFF:
+            stop_note(((double)261.6)*pow(2.0, -4.0)*pow(2.0,(byte1 & 0x7F)/12.0));
+            break;
+    }
+  }
+  if (byte0 == MIDI_STOP) {
+    stop_all_notes();
+  }
+#endif
+}
+
+
+void cc_callback(MidiDevice * device,
+    uint8_t chan, uint8_t num, uint8_t val) {
+  //sending it back on the next channel
+  // midi_send_cc(device, (chan + 1) % 16, num, val);
+}
+
+uint8_t midi_buffer[MIDI_SYSEX_BUFFER] = {0};
+
+void sysex_callback(MidiDevice * device, uint16_t start, uint8_t length, uint8_t * data) {
+    #ifdef API_SYSEX_ENABLE
+        // SEND_STRING("\n");
+        // send_word(start);
+        // SEND_STRING(": ");
+        for (uint8_t place = 0; place < length; place++) {
+            // send_byte(*data);
+            midi_buffer[start + place] = *data;
+            if (*data == 0xF7) {
+                // SEND_STRING("\nRD: ");
+                // for (uint8_t i = 0; i < start + place + 1; i++){
+                //     send_byte(midi_buffer[i]);
+                // SEND_STRING(" ");
+                // }
+                uint8_t * decoded = malloc(sizeof(uint8_t) * (sysex_decoded_length(start + place - 4)));
+                uint16_t decode_length = sysex_decode(decoded, midi_buffer + 4, start + place - 4);
+                process_api(decode_length, decoded);
+            }
+            // SEND_STRING(" ");
+            data++;
+        }
+    #endif
+}
+
+
+#endif