#include "hook.h"
#include "timer.h"
-#ifdef LUFA_DEBUG_SUART
+#ifdef TMK_LUFA_DEBUG_SUART
#include "avr/suart.h"
#endif
+#ifdef TMK_LUFA_DEBUG_UART
+#include "uart.h"
+#endif
+
#include "matrix.h"
#include "descriptor.h"
#include "lufa.h"
-//#define LUFA_DEBUG
+//#define TMK_LUFA_DEBUG
uint8_t keyboard_idle = 0;
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)
+ if (USB_DeviceState != DEVICE_STATE_Configured && !ringbuf_is_full(&sendbuf))
goto EXIT;
- uint8_t ep = Endpoint_GetCurrentEndpoint();
+ if (!console_is_ready() && !ringbuf_is_full(&sendbuf))
+ goto EXIT;
+ /* Data lost considerations:
+ * 1. When buffer is full at early satage of startup, we will have to start sending
+ * before console_is_ready() returns true. Data can be lost even if sending data
+ * seems to be successful on USB. hid_listen on host is not ready perhaps?
+ * Sometime first few packets are lost when buffer is full at startup.
+ * 2. When buffer is full and USB pipe is not ready, new coming data will be lost.
+ * 3. console_task() cannot send data in buffer while main loop is blocked.
+ */
+ /* retry timeout */
+ const uint8_t CONSOLE_TIMOUT = 5; // 1 is too small, 2 seems to be enough for Linux
+ static uint8_t timeout = CONSOLE_TIMOUT;
+ uint16_t prev = timer_read();
+ bool done = false;
+
+ uint8_t ep = Endpoint_GetCurrentEndpoint();
Endpoint_SelectEndpoint(CONSOLE_IN_EPNUM);
- if (!Endpoint_IsEnabled() || !Endpoint_IsConfigured()) {
+
+AGAIN:
+ if (Endpoint_IsStalled() || !Endpoint_IsEnabled() || !Endpoint_IsConfigured()) {
goto EXIT_RESTORE_EP;
}
// clear bank when it is full
if (!Endpoint_IsReadWriteAllowed() && Endpoint_IsINReady()) {
Endpoint_ClearIN();
+ timeout = CONSOLE_TIMOUT; // re-enable retry only when host can receive
}
}
// write c to bank directly if there is no others in buffer
if (ringbuf_is_empty(&sendbuf) && Endpoint_IsReadWriteAllowed()) {
Endpoint_Write_8(c);
+ done = true;
+ }
+
+ // 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();
+ timeout = CONSOLE_TIMOUT; // re-enable retry only when host can receive
+ }
+
+ if (done) {
Endpoint_SelectEndpoint(ep);
return true;
}
+ /* retry when buffer is full.
+ * once timeout this is disabled until host receives actually,
+ * otherwise this will block or make main loop execution sluggish.
+ */
+ if (ringbuf_is_full(&sendbuf) && timeout) {
+ uint16_t curr = timer_read();
+ if (curr != prev) {
+ timeout--;
+ prev = curr;
+ }
+ goto AGAIN;
+ }
+
EXIT_RESTORE_EP:
Endpoint_SelectEndpoint(ep);
EXIT:
*/
void EVENT_USB_Device_Connect(void)
{
+#ifdef TMK_LUFA_DEBUG
print("[C]");
+#endif
/* For battery powered device */
if (!USB_IsInitialized) {
USB_Disable();
USB_Init();
- USB_Device_EnableSOFEvents();
}
}
void EVENT_USB_Device_Disconnect(void)
{
+#ifdef TMK_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.
void EVENT_USB_Device_Reset(void)
{
-#ifdef LUFA_DEBUG
+#ifdef TMK_LUFA_DEBUG
print("[R]");
#endif
}
void EVENT_USB_Device_Suspend()
{
-#ifdef LUFA_DEBUG
+#ifdef TMK_LUFA_DEBUG
print("[S]");
#endif
hook_usb_suspend_entry();
void EVENT_USB_Device_WakeUp()
{
-#ifdef LUFA_DEBUG
+#ifdef TMK_LUFA_DEBUG
print("[W]");
#endif
hook_usb_wakeup();
}
-// called every 1ms
-void EVENT_USB_Device_StartOfFrame(void)
-{
-}
-
/** Event handler for the USB_ConfigurationChanged event.
* This is fired when the host sets the current configuration of the USB device after enumeration.
*
*/
void EVENT_USB_Device_ConfigurationChanged(void)
{
-#ifdef LUFA_DEBUG
+#ifdef TMK_LUFA_DEBUG
print("[c]");
#endif
bool ConfigSuccess = true;
/* Write the report data to the control endpoint */
Endpoint_Write_Control_Stream_LE(ReportData, ReportSize);
Endpoint_ClearOUT();
-#ifdef LUFA_DEBUG
+#ifdef TMK_LUFA_DEBUG
xprintf("[r%d]", USB_ControlRequest.wIndex);
#endif
}
Endpoint_ClearOUT();
Endpoint_ClearStatusStage();
-#ifdef LUFA_DEBUG
+#ifdef TMK_LUFA_DEBUG
xprintf("[L%d]", USB_ControlRequest.wIndex);
#endif
break;
Endpoint_Write_8(keyboard_protocol);
Endpoint_ClearIN();
Endpoint_ClearStatusStage();
-#ifdef LUFA_DEBUG
+#ifdef TMK_LUFA_DEBUG
print("[p]");
#endif
}
keyboard_protocol = (USB_ControlRequest.wValue & 0xFF);
clear_keyboard();
-#ifdef LUFA_DEBUG
+#ifdef TMK_LUFA_DEBUG
print("[P]");
#endif
}
Endpoint_ClearStatusStage();
keyboard_idle = ((USB_ControlRequest.wValue & 0xFF00) >> 8);
-#ifdef LUFA_DEBUG
+#ifdef TMK_LUFA_DEBUG
xprintf("[I%d]%d", USB_ControlRequest.wIndex, (USB_ControlRequest.wValue & 0xFF00) >> 8);
#endif
}
Endpoint_Write_8(keyboard_idle);
Endpoint_ClearIN();
Endpoint_ClearStatusStage();
-#ifdef LUFA_DEBUG
+#ifdef TMK_LUFA_DEBUG
print("[i]");
#endif
}
report_extra_t r = {
.report_id = REPORT_ID_SYSTEM,
- .usage = data
+ .usage = data - SYSTEM_POWER_DOWN + 1
};
Endpoint_SelectEndpoint(EXTRAKEY_IN_EPNUM);
/*******************************************************************************
* sendchar
******************************************************************************/
-#ifdef CONSOLE_ENABLE
int8_t sendchar(uint8_t c)
{
- #ifdef LUFA_DEBUG_SUART
+ #ifdef TMK_LUFA_DEBUG_SUART
xmit(c);
#endif
- bool r = console_putc(c);
- return (r ? 0 : -1);
-}
-#else
-int8_t sendchar(uint8_t c)
-{
- #ifdef LUFA_DEBUG_SUART
- xmit(c);
+ #ifdef TMK_LUFA_DEBUG_UART
+ uart_putchar(c);
+ #endif
+
+ #ifdef CONSOLE_ENABLE
+ console_putc(c);
#endif
+
return 0;
}
-#endif
/*******************************************************************************
USB_Disable();
USB_Init();
-
- USB_Device_EnableSOFEvents();
}
int main(void) __attribute__ ((weak));
{
setup_mcu();
-#ifdef LUFA_DEBUG_SUART
+#ifdef TMK_LUFA_DEBUG_SUART
SUART_OUT_DDR |= (1<<SUART_OUT_BIT);
SUART_OUT_PORT |= (1<<SUART_OUT_BIT);
#endif
+#ifdef TMK_LUFA_DEBUG_UART
+ uart_init(115200);
+#endif
+
// setup sendchar: DO NOT USE print functions before this line
print_set_sendchar(sendchar);
host_set_driver(&lufa_driver);
- print("Keyboard init.\n");
+ print("\n\nTMK:" STR(TMK_VERSION) "/LUFA\n\n");
hook_early_init();
keyboard_setup();
setup_usb();
sei();
+ keyboard_init();
+
+#ifndef NO_USB_STARTUP_WAIT_LOOP
/* wait for USB startup */
while (USB_DeviceState != DEVICE_STATE_Configured) {
#if defined(INTERRUPT_CONTROL_ENDPOINT)
#else
USB_USBTask();
#endif
+ hook_usb_startup_wait_loop();
}
-
- keyboard_init();
+ print("\nUSB configured.\n");
+#endif
hook_late_init();
print("\nKeyboard start.\n");
while (1) {
+#ifndef NO_USB_SUSPEND_LOOP
while (USB_DeviceState == DEVICE_STATE_Suspended) {
-#ifdef LUFA_DEBUG
- print("[s]");
-#endif
hook_usb_suspend_loop();
}
+#endif
keyboard_task();
__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
+ // Turn off LED to save power and keep its status to resotre it later.
+ // LED status will be updated by keyboard_task() in main loop hopefully.
_led_stats = keyboard_led_stats;
keyboard_led_stats = 0;
- led_set(keyboard_led_stats);
+
+ // Calling long task here can prevent USB state transition
matrix_clear();
clear_keyboard();
__attribute__((weak))
void hook_usb_suspend_loop(void)
{
+#ifndef TMK_LUFA_DEBUG_UART
+ // This corrupts debug print when suspend
suspend_power_down();
+#endif
if (USB_Device_RemoteWakeupEnabled && suspend_wakeup_condition()) {
USB_Device_SendRemoteWakeup();
}
sleep_led_disable();
#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
+ // Restore LED status and update at keyboard_task() in main loop
keyboard_led_stats = _led_stats;
+
+ // Calling long task here can prevent USB state transition
}
+
+__attribute__((weak))
+void hook_usb_startup_wait_loop(void) {}