]> git.friedersdorff.com Git - max/tmk_keyboard.git/blobdiff - tmk_core/protocol/lufa/lufa.c
lufa: Add debug print support with UART
[max/tmk_keyboard.git] / tmk_core / protocol / lufa / lufa.c
index c16fd1775b83cc1c85066da8a53c1a97fc69b8c0..ffa5cfbde6fea5bf27f52e31a931b22fbd24f629 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 "action.h"
 #include "led.h"
 #include "sendchar.h"
+#include "ringbuf.h"
 #include "debug.h"
 #ifdef SLEEP_LED_ENABLE
 #include "sleep_led.h"
 #endif
 #include "suspend.h"
 #include "hook.h"
+#include "timer.h"
 
 #ifdef LUFA_DEBUG_SUART
 #include "avr/suart.h"
 #endif
 
+#ifdef LUFA_DEBUG_UART
+#include "uart.h"
+#endif
+
+#include "matrix.h"
 #include "descriptor.h"
 #include "lufa.h"
 
+
+//#define LUFA_DEBUG
+
+
 uint8_t keyboard_idle = 0;
 /* 0: Boot Protocol, 1: Report Protocol(default) */
 uint8_t keyboard_protocol = 1;
@@ -84,60 +95,117 @@ host_driver_t lufa_driver = {
  * Console
  ******************************************************************************/
 #ifdef CONSOLE_ENABLE
-static void Console_Task(void)
+#define SENDBUF_SIZE 256
+static uint8_t sbuf[SENDBUF_SIZE];
+static ringbuf_t sendbuf = {
+    .buffer = sbuf,
+    .head = 0,
+    .tail = 0,
+    .size_mask = SENDBUF_SIZE - 1
+};
+
+// TODO: Around 2500ms delay often works anyhoo but proper startup would be better
+// 1000ms delay of hid_listen affects this probably
+/* wait for Console startup */
+static bool console_is_ready(void)
 {
-    /* Device must be connected and configured for the task to run */
+    static bool hid_listen_ready = false;
+    if (!hid_listen_ready) {
+        if (timer_read32() < 2500)
+            return false;
+        hid_listen_ready = true;
+    }
+    return true;
+}
+
+static bool console_putc(uint8_t c)
+{
+    if (!console_is_ready())
+        goto EXIT;
+
+    // return immediately if called while interrupt
+    if (!(SREG & (1<<SREG_I)))
+        goto EXIT;
+
     if (USB_DeviceState != DEVICE_STATE_Configured)
-        return;
+        goto EXIT;
 
     uint8_t ep = Endpoint_GetCurrentEndpoint();
 
-#if 0
-    // TODO: impl receivechar()/recvchar()
-    Endpoint_SelectEndpoint(CONSOLE_OUT_EPNUM);
+    Endpoint_SelectEndpoint(CONSOLE_IN_EPNUM);
+    if (!Endpoint_IsEnabled() || !Endpoint_IsConfigured()) {
+        goto EXIT_RESTORE_EP;
+    }
 
-    /* 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())
-        {
-            /* 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);
+    // write from buffer to endpoint bank
+    while (!ringbuf_is_empty(&sendbuf) && Endpoint_IsReadWriteAllowed()) {
+        Endpoint_Write_8(ringbuf_get(&sendbuf));
+
+        // clear bank when it is full
+        if (!Endpoint_IsReadWriteAllowed() && Endpoint_IsINReady()) {
+            Endpoint_ClearIN();
         }
+    }
 
-        /* Finalize the stream transfer to send the last packet */
-        Endpoint_ClearOUT();
+    // write c to bank directly if there is no others in buffer
+    if (ringbuf_is_empty(&sendbuf) && Endpoint_IsReadWriteAllowed()) {
+        Endpoint_Write_8(c);
+        Endpoint_SelectEndpoint(ep);
+        return true;
     }
-#endif
 
-    /* IN packet */
+EXIT_RESTORE_EP:
+    Endpoint_SelectEndpoint(ep);
+EXIT:
+    return ringbuf_put(&sendbuf, c);
+}
+
+static void console_flush(void)
+{
+    if (!console_is_ready())
+        return;
+
+    if (USB_DeviceState != DEVICE_STATE_Configured)
+        return;
+
+    uint8_t ep = Endpoint_GetCurrentEndpoint();
+
     Endpoint_SelectEndpoint(CONSOLE_IN_EPNUM);
     if (!Endpoint_IsEnabled() || !Endpoint_IsConfigured()) {
         Endpoint_SelectEndpoint(ep);
         return;
     }
 
-    // fill empty bank
-    while (Endpoint_IsReadWriteAllowed())
-        Endpoint_Write_8(0);
+    // write from buffer to endpoint bank
+    while (!ringbuf_is_empty(&sendbuf) && Endpoint_IsReadWriteAllowed()) {
+        Endpoint_Write_8(ringbuf_get(&sendbuf));
 
-    // flash senchar packet
-    if (Endpoint_IsINReady()) {
+        // clear bank when it is full
+        if (!Endpoint_IsReadWriteAllowed() && Endpoint_IsINReady()) {
+            Endpoint_ClearIN();
+        }
+    }
+
+    // clear bank when there are chars in bank
+    if (Endpoint_BytesInEndpoint() && Endpoint_IsINReady()) {
+        // Windows needs to fill packet with 0
+        while (Endpoint_IsReadWriteAllowed()) {
+                Endpoint_Write_8(0);
+        }
         Endpoint_ClearIN();
     }
 
     Endpoint_SelectEndpoint(ep);
 }
-#else
-static void Console_Task(void)
+
+static void console_task(void)
 {
+    static uint16_t fn = 0;
+    if (fn == USB_Device_GetFrameNumber()) {
+        return;
+    }
+    fn = USB_Device_GetFrameNumber();
+    console_flush();
 }
 #endif
 
@@ -154,7 +222,9 @@ static void Console_Task(void)
 */
 void EVENT_USB_Device_Connect(void)
 {
+#ifdef LUFA_DEBUG
     print("[C]");
+#endif
     /* For battery powered device */
     if (!USB_IsInitialized) {
         USB_Disable();
@@ -165,10 +235,12 @@ void EVENT_USB_Device_Connect(void)
 
 void EVENT_USB_Device_Disconnect(void)
 {
+#ifdef LUFA_DEBUG
     print("[D]");
+#endif
     /* 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();
@@ -179,39 +251,31 @@ void EVENT_USB_Device_Disconnect(void)
 
 void EVENT_USB_Device_Reset(void)
 {
+#ifdef LUFA_DEBUG
     print("[R]");
+#endif
 }
 
 void EVENT_USB_Device_Suspend()
 {
+#ifdef LUFA_DEBUG
     print("[S]");
+#endif
     hook_usb_suspend_entry();
 }
 
 void EVENT_USB_Device_WakeUp()
 {
+#ifdef LUFA_DEBUG
     print("[W]");
+#endif
     hook_usb_wakeup();
 }
 
-#ifdef CONSOLE_ENABLE
-static bool console_flush = false;
-#define CONSOLE_FLUSH_SET(b)   do { \
-    uint8_t sreg = SREG; cli(); console_flush = b; SREG = sreg; \
-} while (0)
-
 // called every 1ms
 void EVENT_USB_Device_StartOfFrame(void)
 {
-    static uint8_t count;
-    if (++count % 50) return;
-    count = 0;
-
-    if (!console_flush) return;
-    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.
@@ -221,7 +285,9 @@ void EVENT_USB_Device_StartOfFrame(void)
  */
 void EVENT_USB_Device_ConfigurationChanged(void)
 {
+#ifdef LUFA_DEBUG
     print("[c]");
+#endif
     bool ConfigSuccess = true;
 
     /* Setup Keyboard HID Report Endpoints */
@@ -275,7 +341,6 @@ Other Device    Required    Optional    Optional    Optional    Optional    Opti
  */
 void EVENT_USB_Device_ControlRequest(void)
 {
-    print("[r]");
     uint8_t* ReportData = NULL;
     uint8_t  ReportSize = 0;
 
@@ -299,6 +364,9 @@ void EVENT_USB_Device_ControlRequest(void)
                 /* Write the report data to the control endpoint */
                 Endpoint_Write_Control_Stream_LE(ReportData, ReportSize);
                 Endpoint_ClearOUT();
+#ifdef LUFA_DEBUG
+                xprintf("[r%d]", USB_ControlRequest.wIndex);
+#endif
             }
 
             break;
@@ -322,6 +390,9 @@ void EVENT_USB_Device_ControlRequest(void)
 
                     Endpoint_ClearOUT();
                     Endpoint_ClearStatusStage();
+#ifdef LUFA_DEBUG
+                    xprintf("[L%d]", USB_ControlRequest.wIndex);
+#endif
                     break;
                 }
 
@@ -338,6 +409,9 @@ void EVENT_USB_Device_ControlRequest(void)
                     Endpoint_Write_8(keyboard_protocol);
                     Endpoint_ClearIN();
                     Endpoint_ClearStatusStage();
+#ifdef LUFA_DEBUG
+                    print("[p]");
+#endif
                 }
             }
 
@@ -351,6 +425,9 @@ void EVENT_USB_Device_ControlRequest(void)
 
                     keyboard_protocol = (USB_ControlRequest.wValue & 0xFF);
                     clear_keyboard();
+#ifdef LUFA_DEBUG
+                    print("[P]");
+#endif
                 }
             }
 
@@ -362,6 +439,9 @@ void EVENT_USB_Device_ControlRequest(void)
                 Endpoint_ClearStatusStage();
 
                 keyboard_idle = ((USB_ControlRequest.wValue & 0xFF00) >> 8);
+#ifdef LUFA_DEBUG
+                xprintf("[I%d]%d", USB_ControlRequest.wIndex, (USB_ControlRequest.wValue & 0xFF00) >> 8);
+#endif
             }
 
             break;
@@ -373,6 +453,9 @@ void EVENT_USB_Device_ControlRequest(void)
                 Endpoint_Write_8(keyboard_idle);
                 Endpoint_ClearIN();
                 Endpoint_ClearStatusStage();
+#ifdef LUFA_DEBUG
+                print("[i]");
+#endif
             }
 
             break;
@@ -380,7 +463,7 @@ void EVENT_USB_Device_ControlRequest(void)
 }
 
 /*******************************************************************************
- * Host driver 
+ * Host driver
  ******************************************************************************/
 static uint8_t keyboard_leds(void)
 {
@@ -389,7 +472,7 @@ static uint8_t keyboard_leds(void)
 
 static void send_keyboard(report_keyboard_t *report)
 {
-    uint8_t timeout = 255;
+    uint8_t timeout = 128;
 
     if (USB_DeviceState != DEVICE_STATE_Configured)
         return;
@@ -401,7 +484,7 @@ static void send_keyboard(report_keyboard_t *report)
         Endpoint_SelectEndpoint(NKRO_IN_EPNUM);
 
         /* Check if write ready for a polling interval around 1ms */
-        while (timeout-- && !Endpoint_IsReadWriteAllowed()) _delay_us(4);
+        while (timeout-- && !Endpoint_IsReadWriteAllowed()) _delay_us(8);
         if (!Endpoint_IsReadWriteAllowed()) return;
 
         /* Write Keyboard Report Data */
@@ -414,7 +497,7 @@ static void send_keyboard(report_keyboard_t *report)
         Endpoint_SelectEndpoint(KEYBOARD_IN_EPNUM);
 
         /* Check if write ready for a polling interval around 10ms */
-        while (timeout-- && !Endpoint_IsReadWriteAllowed()) _delay_us(40);
+        while (timeout-- && !Endpoint_IsReadWriteAllowed()) _delay_us(80);
         if (!Endpoint_IsReadWriteAllowed()) return;
 
         /* Write Keyboard Report Data */
@@ -452,6 +535,7 @@ static void send_mouse(report_mouse_t *report)
 
 static void send_system(uint16_t data)
 {
+#ifdef EXTRAKEY_ENABLE
     uint8_t timeout = 255;
 
     if (USB_DeviceState != DEVICE_STATE_Configured)
@@ -459,7 +543,7 @@ static void send_system(uint16_t data)
 
     report_extra_t r = {
         .report_id = REPORT_ID_SYSTEM,
-        .usage = data
+        .usage = data - SYSTEM_POWER_DOWN + 1
     };
     Endpoint_SelectEndpoint(EXTRAKEY_IN_EPNUM);
 
@@ -469,10 +553,12 @@ static void send_system(uint16_t data)
 
     Endpoint_Write_Stream_LE(&r, sizeof(report_extra_t), NULL);
     Endpoint_ClearIN();
+#endif
 }
 
 static void send_consumer(uint16_t data)
 {
+#ifdef EXTRAKEY_ENABLE
     uint8_t timeout = 255;
 
     if (USB_DeviceState != DEVICE_STATE_Configured)
@@ -490,82 +576,29 @@ static void send_consumer(uint16_t data)
 
     Endpoint_Write_Stream_LE(&r, sizeof(report_extra_t), NULL);
     Endpoint_ClearIN();
+#endif
 }
 
 
 /*******************************************************************************
  * sendchar
  ******************************************************************************/
-#ifdef CONSOLE_ENABLE
-#define SEND_TIMEOUT 5
 int8_t sendchar(uint8_t c)
 {
-#ifdef LUFA_DEBUG_SUART
+    #ifdef LUFA_DEBUG_SUART
     xmit(c);
-#endif
-    // Not wait once timeouted.
-    // Because sendchar() is called so many times, waiting each call causes big lag.
-    static bool timeouted = false;
-
-    // prevents Console_Task() from running during sendchar() runs.
-    // or char will be lost. These two function is mutually exclusive.
-    CONSOLE_FLUSH_SET(false);
-
-    if (USB_DeviceState != DEVICE_STATE_Configured)
-        return -1;
-
-    uint8_t ep = Endpoint_GetCurrentEndpoint();
-    Endpoint_SelectEndpoint(CONSOLE_IN_EPNUM);
-    if (!Endpoint_IsEnabled() || !Endpoint_IsConfigured()) {
-        goto ERROR_EXIT;
-    }
+    #endif
 
-    if (timeouted && !Endpoint_IsReadWriteAllowed()) {
-        goto ERROR_EXIT;
-    }
+    #ifdef LUFA_DEBUG_UART
+    uart_putchar(c);
+    #endif
 
-    timeouted = false;
+    #ifdef CONSOLE_ENABLE
+    console_putc(c);
+    #endif
 
-    uint8_t timeout = SEND_TIMEOUT;
-    while (!Endpoint_IsReadWriteAllowed()) {
-        if (USB_DeviceState != DEVICE_STATE_Configured) {
-            goto ERROR_EXIT;
-        }
-        if (Endpoint_IsStalled()) {
-            goto ERROR_EXIT;
-        }
-        if (!(timeout--)) {
-            timeouted = true;
-            goto ERROR_EXIT;
-        }
-        _delay_ms(1);
-    }
-
-    Endpoint_Write_8(c);
-
-    // send when bank is full
-    if (!Endpoint_IsReadWriteAllowed()) {
-        while (!(Endpoint_IsINReady()));
-        Endpoint_ClearIN();
-    } else {
-        CONSOLE_FLUSH_SET(true);
-    }
-
-    Endpoint_SelectEndpoint(ep);
     return 0;
-ERROR_EXIT:
-    Endpoint_SelectEndpoint(ep);
-    return -1;
 }
-#else
-int8_t sendchar(uint8_t c)
-{
-#ifdef LUFA_DEBUG_SUART
-    xmit(c);
-#endif
-    return 0;
-}
-#endif
 
 
 /*******************************************************************************
@@ -588,50 +621,66 @@ static void setup_usb(void)
 
     USB_Init();
 
-    // for Console_Task
     USB_Device_EnableSOFEvents();
 }
 
 int main(void)  __attribute__ ((weak));
 int main(void)
 {
+    setup_mcu();
+
 #ifdef LUFA_DEBUG_SUART
-    DDRD |= (1<<SUART_OUT_BIT);
+    SUART_OUT_DDR |= (1<<SUART_OUT_BIT);
+    SUART_OUT_PORT |= (1<<SUART_OUT_BIT);
+#endif
+
+#ifdef LUFA_DEBUG_UART
+    uart_init(115200);
 #endif
+
+    // setup sendchar: DO NOT USE print functions before this line
     print_set_sendchar(sendchar);
-    setup_mcu();
+    host_set_driver(&lufa_driver);
+
+    print("Keyboard init.\n");
     hook_early_init();
     keyboard_setup();
     setup_usb();
+#ifdef SLEEP_LED_ENABLE
+    sleep_led_init();
+#endif
+
     sei();
 
-    /* wait for USB startup & debug output */
+    keyboard_init();
+
+    /* wait for USB startup */
     while (USB_DeviceState != DEVICE_STATE_Configured) {
 #if defined(INTERRUPT_CONTROL_ENDPOINT)
         ;
 #else
         USB_USBTask();
 #endif
+        matrix_scan();
     }
-    print("USB configured.\n");
 
-    /* init modules */
-    keyboard_init();
-    host_set_driver(&lufa_driver);
-#ifdef SLEEP_LED_ENABLE
-    sleep_led_init();
-#endif
-
-    print("Keyboard start.\n");
     hook_late_init();
+
+    print("\nKeyboard start.\n");
     while (1) {
         while (USB_DeviceState == DEVICE_STATE_Suspended) {
+#ifdef LUFA_DEBUG
             print("[s]");
+#endif
             hook_usb_suspend_loop();
         }
 
         keyboard_task();
 
+#ifdef CONSOLE_ENABLE
+        console_task();
+#endif
+
 #if !defined(INTERRUPT_CONTROL_ENDPOINT)
         USB_USBTask();
 #endif
@@ -646,9 +695,19 @@ void hook_early_init(void) {}
 __attribute__((weak))
 void hook_late_init(void) {}
 
+static uint8_t _led_stats = 0;
  __attribute__((weak))
 void hook_usb_suspend_entry(void)
 {
+    // Turn LED off to save power
+    // Set 0 with putting aside status before suspend and restore
+    // it after wakeup, then LED is updated at keyboard_task() in main loop
+    _led_stats = keyboard_led_stats;
+    keyboard_led_stats = 0;
+    led_set(keyboard_led_stats);
+
+    matrix_clear();
+    clear_keyboard();
 #ifdef SLEEP_LED_ENABLE
     sleep_led_enable();
 #endif
@@ -659,7 +718,7 @@ void hook_usb_suspend_loop(void)
 {
     suspend_power_down();
     if (USB_Device_RemoteWakeupEnabled && suspend_wakeup_condition()) {
-            USB_Device_SendRemoteWakeup();
+        USB_Device_SendRemoteWakeup();
     }
 }
 
@@ -669,7 +728,12 @@ void hook_usb_wakeup(void)
     suspend_wakeup_init();
 #ifdef SLEEP_LED_ENABLE
     sleep_led_disable();
-    // NOTE: converters may not accept this
-    led_set(host_keyboard_leds());
 #endif
+
+    // Restore LED status
+    // BIOS/grub won't recognize/enumerate if led_set() takes long(around 40ms?)
+    // Converters fall into the case and miss wakeup event(timeout to reply?) in the end.
+    //led_set(host_keyboard_leds());
+    // Instead, restore stats and update at keyboard_task() in main loop
+    keyboard_led_stats = _led_stats;
 }