]> git.friedersdorff.com Git - max/tmk_keyboard.git/blobdiff - tmk_core/protocol/chibios/usb_main.c
make some change to complie stm32_f103_onekey with new version of Chibios (#583)
[max/tmk_keyboard.git] / tmk_core / protocol / chibios / usb_main.c
index f8a31593b039e373dc1fe8a029d6fcbc58f58e08..60e1dd9451a8b382ef647da8c3fc8fc31b872016 100644 (file)
 
 #include "usb_main.h"
 
+#include "host.h"
 #include "debug.h"
+#include "suspend.h"
 #ifdef SLEEP_LED_ENABLE
 #include "sleep_led.h"
+#include "led.h"
 #endif
+#include "hook.h"
+
+/* TMK hooks */
+__attribute__((weak))
+void hook_usb_wakeup(void) {
+#ifdef SLEEP_LED_ENABLE
+    sleep_led_disable();
+    // NOTE: converters may not accept this
+    led_set(host_keyboard_leds());
+#endif /* SLEEP_LED_ENABLE */
+}
+
+ __attribute__((weak))
+void hook_usb_suspend_entry(void) {
+#ifdef SLEEP_LED_ENABLE
+    sleep_led_enable();
+#endif /* SLEEP_LED_ENABLE */
+}
+
 
 /* ---------------------------------------------------------
  *       Global interface variables and declarations
  * ---------------------------------------------------------
  */
 
-uint8_t keyboard_idle = 0;
-uint8_t keyboard_protocol = 1;
-uint16_t keyboard_led_stats = 0;
+uint8_t keyboard_idle __attribute__((aligned(2))) = 0;
+uint8_t keyboard_protocol __attribute__((aligned(2))) = 1;
+uint16_t keyboard_led_stats __attribute__((aligned(2))) = 0;
 volatile uint16_t keyboard_idle_count = 0;
 static virtual_timer_t keyboard_idle_timer;
 static void keyboard_idle_timer_cb(void *arg);
 #ifdef NKRO_ENABLE
-bool keyboard_nkro = true;
+extern bool keyboard_nkro;
 #endif /* NKRO_ENABLE */
 
 report_keyboard_t keyboard_report_sent = {{0}};
+#ifdef MOUSE_ENABLE
+report_mouse_t mouse_report_blank = {0};
+#endif /* MOUSE_ENABLE */
+#ifdef EXTRAKEY_ENABLE
+uint8_t extra_report_blank[3] = {0};
+#endif /* EXTRAKEY_ENABLE */
 
 #ifdef CONSOLE_ENABLE
-/* The emission queue */
-output_queue_t console_queue;
-static uint8_t console_queue_buffer[CONSOLE_QUEUE_BUFFER_SIZE];
+/* The emission buffers queue */
+output_buffers_queue_t console_buf_queue;
+static uint8_t console_queue_buffer[BQ_BUFFER_SIZE(CONSOLE_QUEUE_CAPACITY, CONSOLE_EPSIZE)];
+
 static virtual_timer_t console_flush_timer;
-void console_queue_onotify(io_queue_t *qp);
+void console_queue_onotify(io_buffers_queue_t *bqp);
 static void console_flush_cb(void *arg);
 #endif /* CONSOLE_ENABLE */
 
@@ -124,7 +153,7 @@ static const uint8_t keyboard_hid_report_desc_data[] = {
   0x95, KBD_REPORT_KEYS,          //   Report Count (),
   0x75, 0x08,                //   Report Size (8),
   0x15, 0x00,                //   Logical Minimum (0),
-  0x25, 0xFF,                //   Logical Maximum(255),
+  0x26, 0xFF, 0x00,          //   Logical Maximum(255),
   0x05, 0x07,                //   Usage Page (Key Codes),
   0x19, 0x00,                //   Usage Minimum (0),
   0x29, 0xFF,                //   Usage Maximum (255),
@@ -248,7 +277,7 @@ static const uint8_t console_hid_report_desc_data[] = {
   0x75, 0x08,       // report size = 8 bits
   0x15, 0x00,       // logical minimum = 0
   0x26, 0xFF, 0x00, // logical maximum = 255
-  0x95, CONSOLE_SIZE, // report count
+  0x95, CONSOLE_EPSIZE, // report count
   0x09, 0x75,       // usage
   0x81, 0x02,       // Input (array)
   0xC0              // end collection
@@ -270,7 +299,7 @@ static const uint8_t extra_hid_report_desc_data[] = {
   0xa1, 0x01,                      // COLLECTION (Application)
   0x85, REPORT_ID_SYSTEM,          //   REPORT_ID (2)
   0x15, 0x01,                      //   LOGICAL_MINIMUM (0x1)
-  0x25, 0xb7,                      //   LOGICAL_MAXIMUM (0xb7)
+  0x26, 0xb7, 0x00,                //   LOGICAL_MAXIMUM (0xb7)
   0x19, 0x01,                      //   USAGE_MINIMUM (0x1)
   0x29, 0xb7,                      //   USAGE_MAXIMUM (0xb7)
   0x75, 0x10,                      //   REPORT_SIZE (16)
@@ -348,8 +377,8 @@ static const uint8_t hid_configuration_descriptor_data[] = {
                          NUM_INTERFACES,    // bNumInterfaces
                          1,    // bConfigurationValue
                          0,    // iConfiguration
-                         0xA0, // bmAttributes
-                         50),  // bMaxPower (100mA)
+                         0xA0, // bmAttributes (RESERVED|REMOTEWAKEUP)
+                         50),  // bMaxPower (50mA)
 
   /* Interface Descriptor (9 bytes) USB spec 9.6.5, page 267-269, Table 9-12 */
   USB_DESC_INTERFACE(KBD_INTERFACE,        // bInterfaceNumber
@@ -372,7 +401,7 @@ static const uint8_t hid_configuration_descriptor_data[] = {
   /* Endpoint Descriptor (7 bytes) USB spec 9.6.6, page 269-271, Table 9-13 */
   USB_DESC_ENDPOINT(KBD_ENDPOINT | 0x80,  // bEndpointAddress
                     0x03,      // bmAttributes (Interrupt)
-                    KBD_SIZE,  // wMaxPacketSize
+                    KBD_EPSIZE,// wMaxPacketSize
                     10),       // bInterval
 
   #ifdef MOUSE_ENABLE
@@ -402,7 +431,7 @@ static const uint8_t hid_configuration_descriptor_data[] = {
   /* Endpoint Descriptor (7 bytes) USB spec 9.6.6, page 269-271, Table 9-13 */
   USB_DESC_ENDPOINT(MOUSE_ENDPOINT | 0x80,  // bEndpointAddress
                     0x03,      // bmAttributes (Interrupt)
-                    MOUSE_SIZE,  // wMaxPacketSize
+                    MOUSE_EPSIZE,  // wMaxPacketSize
                     1),        // bInterval
   #endif /* MOUSE_ENABLE */
 
@@ -428,7 +457,7 @@ static const uint8_t hid_configuration_descriptor_data[] = {
   /* Endpoint Descriptor (7 bytes) USB spec 9.6.6, page 269-271, Table 9-13 */
   USB_DESC_ENDPOINT(CONSOLE_ENDPOINT | 0x80,  // bEndpointAddress
                     0x03,      // bmAttributes (Interrupt)
-                    CONSOLE_SIZE, // wMaxPacketSize
+                    CONSOLE_EPSIZE, // wMaxPacketSize
                     1),        // bInterval
   #endif /* CONSOLE_ENABLE */
 
@@ -454,7 +483,7 @@ static const uint8_t hid_configuration_descriptor_data[] = {
   /* Endpoint Descriptor (7 bytes) USB spec 9.6.6, page 269-271, Table 9-13 */
   USB_DESC_ENDPOINT(EXTRA_ENDPOINT | 0x80,  // bEndpointAddress
                     0x03,      // bmAttributes (Interrupt)
-                    EXTRA_SIZE, // wMaxPacketSize
+                    EXTRA_EPSIZE, // wMaxPacketSize
                     10),       // bInterval
   #endif /* EXTRAKEY_ENABLE */
 
@@ -480,7 +509,7 @@ static const uint8_t hid_configuration_descriptor_data[] = {
   /* Endpoint Descriptor (7 bytes) USB spec 9.6.6, page 269-271, Table 9-13 */
   USB_DESC_ENDPOINT(NKRO_ENDPOINT | 0x80,  // bEndpointAddress
                     0x03,      // bmAttributes (Interrupt)
-                    NKRO_SIZE, // wMaxPacketSize
+                    NKRO_EPSIZE, // wMaxPacketSize
                     1),       // bInterval
   #endif /* NKRO_ENABLE */
 };
@@ -663,7 +692,7 @@ static const USBEndpointConfig kbd_ep_config = {
   NULL,                         /* SETUP packet notification callback */
   kbd_in_cb,                    /* IN notification callback */
   NULL,                         /* OUT notification callback */
-  KBD_SIZE,                     /* IN maximum packet size */
+  KBD_EPSIZE,                   /* IN maximum packet size */
   0,                            /* OUT maximum packet size */
   &kbd_ep_state,                /* IN Endpoint state */
   NULL,                         /* OUT endpoint state */
@@ -681,7 +710,7 @@ static const USBEndpointConfig mouse_ep_config = {
   NULL,                         /* SETUP packet notification callback */
   mouse_in_cb,                  /* IN notification callback */
   NULL,                         /* OUT notification callback */
-  MOUSE_SIZE,                   /* IN maximum packet size */
+  MOUSE_EPSIZE,                 /* IN maximum packet size */
   0,                            /* OUT maximum packet size */
   &mouse_ep_state,              /* IN Endpoint state */
   NULL,                         /* OUT endpoint state */
@@ -700,7 +729,7 @@ static const USBEndpointConfig console_ep_config = {
   NULL,                         /* SETUP packet notification callback */
   console_in_cb,                /* IN notification callback */
   NULL,                         /* OUT notification callback */
-  CONSOLE_SIZE,                 /* IN maximum packet size */
+  CONSOLE_EPSIZE,               /* IN maximum packet size */
   0,                            /* OUT maximum packet size */
   &console_ep_state,            /* IN Endpoint state */
   NULL,                         /* OUT endpoint state */
@@ -719,7 +748,7 @@ static const USBEndpointConfig extra_ep_config = {
   NULL,                         /* SETUP packet notification callback */
   extra_in_cb,                  /* IN notification callback */
   NULL,                         /* OUT notification callback */
-  EXTRA_SIZE,                   /* IN maximum packet size */
+  EXTRA_EPSIZE,                 /* IN maximum packet size */
   0,                            /* OUT maximum packet size */
   &extra_ep_state,              /* IN Endpoint state */
   NULL,                         /* OUT endpoint state */
@@ -738,7 +767,7 @@ static const USBEndpointConfig nkro_ep_config = {
   NULL,                         /* SETUP packet notification callback */
   nkro_in_cb,                   /* IN notification callback */
   NULL,                         /* OUT notification callback */
-  NKRO_SIZE,                    /* IN maximum packet size */
+  NKRO_EPSIZE,                  /* IN maximum packet size */
   0,                            /* OUT maximum packet size */
   &nkro_ep_state,               /* IN Endpoint state */
   NULL,                         /* OUT endpoint state */
@@ -785,20 +814,13 @@ static void usb_event_cb(USBDriver *usbp, usbevent_t event) {
 
   case USB_EVENT_SUSPEND:
     //TODO: from ISR! print("[S]");
-    //TODO: signal suspend?
-#ifdef SLEEP_LED_ENABLE
-    sleep_led_enable();
-#endif /* SLEEP_LED_ENABLE */
+    hook_usb_suspend_entry();
     return;
 
   case USB_EVENT_WAKEUP:
     //TODO: from ISR! print("[W]");
-    //TODO: suspend_wakeup_init();
-#ifdef SLEEP_LED_ENABLE
-    sleep_led_disable();
-    // NOTE: converters may not accept this
-    led_set(host_keyboard_leds());
-#endif /* SLEEP_LED_ENABLE */
+    suspend_wakeup_init();
+    hook_usb_wakeup();
     return;
 
   case USB_EVENT_STALLED:
@@ -856,6 +878,43 @@ static bool usb_request_hook_cb(USBDriver *usbp) {
           return TRUE;
           break;
 
+#ifdef MOUSE_ENABLE
+        case MOUSE_INTERFACE:
+          usbSetupTransfer(usbp, (uint8_t *)&mouse_report_blank, sizeof(mouse_report_blank), NULL);
+          return TRUE;
+          break;
+#endif /* MOUSE_ENABLE */
+
+#ifdef CONSOLE_ENABLE
+        case CONSOLE_INTERFACE:
+          usbSetupTransfer(usbp, console_queue_buffer, CONSOLE_EPSIZE, NULL);
+          return TRUE;
+          break;
+#endif /* CONSOLE_ENABLE */
+
+#ifdef EXTRAKEY_ENABLE
+        case EXTRA_INTERFACE:
+          if(usbp->setup[3] == 1) { /* MSB(wValue) [Report Type] == 1 [Input Report] */
+            switch(usbp->setup[2]) { /* LSB(wValue) [Report ID] */
+              case REPORT_ID_SYSTEM:
+                extra_report_blank[0] = REPORT_ID_SYSTEM;
+                usbSetupTransfer(usbp, (uint8_t *)extra_report_blank, sizeof(extra_report_blank), NULL);
+                return TRUE;
+                break;
+              case REPORT_ID_CONSUMER:
+                extra_report_blank[0] = REPORT_ID_CONSUMER;
+                usbSetupTransfer(usbp, (uint8_t *)extra_report_blank, sizeof(extra_report_blank), NULL);
+                return TRUE;
+                break;
+              default:
+                return FALSE;
+            }
+          } else {
+            return FALSE;
+          }
+          break;
+#endif /* EXTRAKEY_ENABLE */
+
         default:
           usbSetupTransfer(usbp, NULL, 0, NULL);
           return TRUE;
@@ -904,7 +963,7 @@ static bool usb_request_hook_cb(USBDriver *usbp) {
 #endif /* NKRO_ENABLE */
           /* arm the idle timer if boot protocol & idle */
             osalSysLockFromISR();
-            chVTSetI(&keyboard_idle_timer, 4*MS2ST(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp);
+            chVTSetI(&keyboard_idle_timer, 4*TIME_MS2I(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp);
             osalSysUnlockFromISR();
           }
         }
@@ -921,7 +980,7 @@ static bool usb_request_hook_cb(USBDriver *usbp) {
         if(keyboard_idle) {
 #endif /* NKRO_ENABLE */
           osalSysLockFromISR();
-          chVTSetI(&keyboard_idle_timer, 4*MS2ST(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp);
+          chVTSetI(&keyboard_idle_timer, 4*TIME_MS2I(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp);
           osalSysUnlockFromISR();
         }
         usbSetupTransfer(usbp, NULL, 0, NULL);
@@ -974,11 +1033,32 @@ void init_usb_driver(USBDriver *usbp) {
 
   chVTObjectInit(&keyboard_idle_timer);
 #ifdef CONSOLE_ENABLE
-  oqObjectInit(&console_queue, console_queue_buffer, sizeof(console_queue_buffer), console_queue_onotify, (void *)usbp);
+  obqObjectInit(&console_buf_queue, true, console_queue_buffer, CONSOLE_EPSIZE, CONSOLE_QUEUE_CAPACITY, console_queue_onotify, (void*)usbp);
   chVTObjectInit(&console_flush_timer);
 #endif
 }
 
+/*
+ * Send remote wakeup packet
+ * Note: should not be called from ISR
+ */
+void send_remote_wakeup(USBDriver *usbp) {
+  (void)usbp;
+#if defined(K20x) || defined(KL2x)
+#if KINETIS_USB_USE_USB0
+  USB0->CTL |= USBx_CTL_RESUME;
+  chThdSleepMilliseconds(15);
+  USB0->CTL &= ~USBx_CTL_RESUME;
+#endif /* KINETIS_USB_USE_USB0 */
+#elif defined(STM32F0XX) || defined(STM32F1XX) /* K20x || KL2x */
+  STM32_USB->CNTR |= CNTR_RESUME;
+  chThdSleepMilliseconds(15);
+  STM32_USB->CNTR &= ~CNTR_RESUME;
+#else /* STM32F0XX || STM32F1XX */
+#warning Sending remote wakeup packet not implemented for your platform.
+#endif /* K20x || KL2x */
+}
+
 /* ---------------------------------------------------------
  *                  Keyboard functions
  * ---------------------------------------------------------
@@ -1027,12 +1107,11 @@ static void keyboard_idle_timer_cb(void *arg) {
   if(keyboard_idle) {
 #endif /* NKRO_ENABLE */
     /* TODO: are we sure we want the KBD_ENDPOINT? */
-    osalSysUnlockFromISR();
-    usbPrepareTransmit(usbp, KBD_ENDPOINT, (uint8_t *)&keyboard_report_sent, sizeof(keyboard_report_sent));
-    osalSysLockFromISR();
-    usbStartTransmitI(usbp, KBD_ENDPOINT);
+    if(!usbGetTransmitStatusI(usbp, KBD_ENDPOINT)) {
+      usbStartTransmitI(usbp, KBD_ENDPOINT, (uint8_t *)&keyboard_report_sent, KBD_EPSIZE);
+    }
     /* rearm the timer */
-    chVTSetI(&keyboard_idle_timer, 4*MS2ST(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp);
+    chVTSetI(&keyboard_idle_timer, 4*TIME_MS2I(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp);
   }
 
   /* do not rearm the timer if the condition above fails
@@ -1057,16 +1136,35 @@ void send_keyboard(report_keyboard_t *report) {
 
 #ifdef NKRO_ENABLE
   if(keyboard_nkro) {  /* NKRO protocol */
-    usbPrepareTransmit(&USB_DRIVER, NKRO_ENDPOINT, (uint8_t *)report, sizeof(report_keyboard_t));
+    /* need to wait until the previous packet has made it through */
+    /* can rewrite this using the synchronous API, then would wait
+     * until *after* the packet has been transmitted. I think
+     * this is more efficient */
+    /* busy wait, should be short and not very common */
     osalSysLock();
-    usbStartTransmitI(&USB_DRIVER, NKRO_ENDPOINT);
+    if(usbGetTransmitStatusI(&USB_DRIVER, NKRO_ENDPOINT)) {
+      /* Need to either suspend, or loop and call unlock/lock during
+       * every iteration - otherwise the system will remain locked,
+       * no interrupts served, so USB not going through as well.
+       * Note: for suspend, need USB_USE_WAIT == TRUE in halconf.h */
+      osalThreadSuspendS(&(&USB_DRIVER)->epc[NKRO_ENDPOINT]->in_state->thread);
+    }
+    usbStartTransmitI(&USB_DRIVER, NKRO_ENDPOINT, (uint8_t *)report, sizeof(report_keyboard_t));
     osalSysUnlock();
   } else
 #endif /* NKRO_ENABLE */
   { /* boot protocol */
-    usbPrepareTransmit(&USB_DRIVER, KBD_ENDPOINT, (uint8_t *)report, sizeof(report_keyboard_t));
+    /* need to wait until the previous packet has made it through */
+    /* busy wait, should be short and not very common */
     osalSysLock();
-    usbStartTransmitI(&USB_DRIVER, KBD_ENDPOINT);
+    if(usbGetTransmitStatusI(&USB_DRIVER, KBD_ENDPOINT)) {
+      /* Need to either suspend, or loop and call unlock/lock during
+       * every iteration - otherwise the system will remain locked,
+       * no interrupts served, so USB not going through as well.
+       * Note: for suspend, need USB_USE_WAIT == TRUE in halconf.h */
+      osalThreadSuspendS(&(&USB_DRIVER)->epc[KBD_ENDPOINT]->in_state->thread);
+    }
+    usbStartTransmitI(&USB_DRIVER, KBD_ENDPOINT, (uint8_t *)report, KBD_EPSIZE);
     osalSysUnlock();
   }
   keyboard_report_sent = *report;
@@ -1098,9 +1196,8 @@ void send_mouse(report_mouse_t *report) {
    * is this really needed?
    */
 
-  usbPrepareTransmit(&USB_DRIVER, MOUSE_ENDPOINT, (uint8_t *)report, sizeof(report_mouse_t));
   osalSysLock();
-  usbStartTransmitI(&USB_DRIVER, MOUSE_ENDPOINT);
+  usbStartTransmitI(&USB_DRIVER, MOUSE_ENDPOINT, (uint8_t *)report, sizeof(report_mouse_t));
   osalSysUnlock();
 }
 
@@ -1136,10 +1233,7 @@ static void send_extra_report(uint8_t report_id, uint16_t data) {
     .usage = data
   };
 
-  osalSysUnlock();
-  usbPrepareTransmit(&USB_DRIVER, EXTRA_ENDPOINT, (uint8_t *)&report, sizeof(report_extra_t));
-  osalSysLock();
-  usbStartTransmitI(&USB_DRIVER, EXTRA_ENDPOINT);
+  usbStartTransmitI(&USB_DRIVER, EXTRA_ENDPOINT, (uint8_t *)&report, sizeof(report_extra_t));
   osalSysUnlock();
 }
 
@@ -1167,20 +1261,32 @@ void send_consumer(uint16_t data) {
 
 #ifdef CONSOLE_ENABLE
 
-/* debug IN callback hander */
+/* console IN callback hander */
 void console_in_cb(USBDriver *usbp, usbep_t ep) {
-  (void)ep;
+  (void)ep; /* should have ep == CONSOLE_ENDPOINT, so use that to save time/space */
+  uint8_t *buf;
+  size_t n;
+
   osalSysLockFromISR();
 
   /* rearm the timer */
-  chVTSetI(&console_flush_timer, MS2ST(CONSOLE_FLUSH_MS), console_flush_cb, (void *)usbp);
+  chVTSetI(&console_flush_timer, TIME_MS2I(CONSOLE_FLUSH_MS), console_flush_cb, (void *)usbp);
 
-  /* Check if there is data to send left in the output queue */
-  if(chOQGetFullI(&console_queue) >= CONSOLE_SIZE) {
-    osalSysUnlockFromISR();
-    usbPrepareQueuedTransmit(usbp, CONSOLE_ENDPOINT, &console_queue, CONSOLE_SIZE);
-    osalSysLockFromISR();
-    usbStartTransmitI(usbp, CONSOLE_ENDPOINT);
+  /* Freeing the buffer just transmitted, if it was not a zero size packet.*/
+  if (usbp->epc[CONSOLE_ENDPOINT]->in_state->txsize > 0U) {
+    obqReleaseEmptyBufferI(&console_buf_queue);
+  }
+
+  /* Checking if there is a buffer ready for transmission.*/
+  buf = obqGetFullBufferI(&console_buf_queue, &n);
+
+  if (buf != NULL) {
+    /* The endpoint cannot be busy, we are in the context of the callback,
+       so it is safe to transmit without a check.*/
+    /* Should have n == CONSOLE_EPSIZE; check it? */
+    usbStartTransmitI(usbp, CONSOLE_ENDPOINT, buf, CONSOLE_EPSIZE);
+  } else {
+    /* Nothing to transmit.*/
   }
 
   osalSysUnlockFromISR();
@@ -1188,18 +1294,22 @@ void console_in_cb(USBDriver *usbp, usbep_t ep) {
 
 /* Callback when data is inserted into the output queue
  * Called from a locked state */
-void console_queue_onotify(io_queue_t *qp) {
-  USBDriver *usbp = qGetLink(qp);
+void console_queue_onotify(io_buffers_queue_t *bqp) {
+  size_t n;
+  USBDriver *usbp = bqGetLinkX(bqp);
 
   if(usbGetDriverStateI(usbp) != USB_ACTIVE)
     return;
 
-  if(!usbGetTransmitStatusI(usbp, CONSOLE_ENDPOINT)
-     && (chOQGetFullI(&console_queue) >= CONSOLE_SIZE)) {
-    osalSysUnlock();
-    usbPrepareQueuedTransmit(usbp, CONSOLE_ENDPOINT, &console_queue, CONSOLE_SIZE);
-    osalSysLock();
-    usbStartTransmitI(usbp, CONSOLE_ENDPOINT);
+  /* Checking if there is already a transaction ongoing on the endpoint.*/
+  if (!usbGetTransmitStatusI(usbp, CONSOLE_ENDPOINT)) {
+    /* Trying to get a full buffer.*/
+    uint8_t *buf = obqGetFullBufferI(&console_buf_queue, &n);
+    if (buf != NULL) {
+      /* Buffer found, starting a new transaction.*/
+      /* Should have n == CONSOLE_EPSIZE; check this? */
+      usbStartTransmitI(usbp, CONSOLE_ENDPOINT, buf, CONSOLE_EPSIZE);
+    }
   }
 }
 
@@ -1207,38 +1317,41 @@ void console_queue_onotify(io_queue_t *qp) {
  * callback (called from ISR, unlocked state) */
 static void console_flush_cb(void *arg) {
   USBDriver *usbp = (USBDriver *)arg;
-  size_t i, n;
-  uint8_t buf[CONSOLE_SIZE]; /* TODO: a solution without extra buffer? */
   osalSysLockFromISR();
 
   /* check that the states of things are as they're supposed to */
   if(usbGetDriverStateI(usbp) != USB_ACTIVE) {
     /* rearm the timer */
-    chVTSetI(&console_flush_timer, MS2ST(CONSOLE_FLUSH_MS), console_flush_cb, (void *)usbp);
+    chVTSetI(&console_flush_timer, TIME_MS2I(CONSOLE_FLUSH_MS), console_flush_cb, (void *)usbp);
     osalSysUnlockFromISR();
     return;
   }
 
-  /* don't do anything if the queue is empty or has enough stuff in it */
-  if(((n = oqGetFullI(&console_queue)) == 0) || (n >= CONSOLE_SIZE)) {
+  /* If there is already a transaction ongoing then another one cannot be
+     started.*/
+  if (usbGetTransmitStatusI(usbp, CONSOLE_ENDPOINT)) {
     /* rearm the timer */
-    chVTSetI(&console_flush_timer, MS2ST(CONSOLE_FLUSH_MS), console_flush_cb, (void *)usbp);
+    chVTSetI(&console_flush_timer, TIME_MS2I(CONSOLE_FLUSH_MS), console_flush_cb, (void *)usbp);
     osalSysUnlockFromISR();
     return;
   }
 
-  /* there's stuff hanging in the queue - so dequeue and send */
-  for(i = 0; i < n; i++)
-    buf[i] = (uint8_t)oqGetI(&console_queue);
-  for(i = n; i < CONSOLE_SIZE; i++)
-    buf[i] = 0;
-  osalSysUnlockFromISR();
-  usbPrepareTransmit(usbp, CONSOLE_ENDPOINT, buf, CONSOLE_SIZE);
-  osalSysLockFromISR();
-  (void)usbStartTransmitI(usbp, CONSOLE_ENDPOINT);
+  /* Checking if there only a buffer partially filled, if so then it is
+     enforced in the queue and transmitted.*/
+  if(obqTryFlushI(&console_buf_queue)) {
+    size_t n,i;
+    uint8_t *buf = obqGetFullBufferI(&console_buf_queue, &n);
+
+    osalDbgAssert(buf != NULL, "queue is empty");
+
+    /* zero the rest of the buffer (buf should point to allocated space) */
+    for(i=n; i<CONSOLE_EPSIZE; i++)
+      buf[i]=0;
+    usbStartTransmitI(usbp, CONSOLE_ENDPOINT, buf, CONSOLE_EPSIZE);
+  }
 
   /* rearm the timer */
-  chVTSetI(&console_flush_timer, MS2ST(CONSOLE_FLUSH_MS), console_flush_cb, (void *)usbp);
+  chVTSetI(&console_flush_timer, TIME_MS2I(CONSOLE_FLUSH_MS), console_flush_cb, (void *)usbp);
   osalSysUnlockFromISR();
 }
 
@@ -1250,10 +1363,13 @@ int8_t sendchar(uint8_t c) {
     return 0;
   }
   osalSysUnlock();
-  /* should get suspended and wait if the queue is full
-   * but it's not blocking even if noone is listening,
-   *  because the USB packets are sent anyway */
-  return(chOQPut(&console_queue, c));
+  /* Timeout after 100us if the queue is full.
+   * Increase this timeout if too much stuff is getting
+   * dropped (i.e. the buffer is getting full too fast
+   * for USB/HIDRAW to dequeue). Another possibility
+   * for fixing this kind of thing is to increase
+   * CONSOLE_QUEUE_CAPACITY. */
+  return(obqPutTimeout(&console_buf_queue, c, US2ST(100)));
 }
 
 #else /* CONSOLE_ENABLE */