#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);
#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 */
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),
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)
case USB_EVENT_SUSPEND:
//TODO: from ISR! print("[S]");
-#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]");
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 */
+ hook_usb_wakeup();
return;
case USB_EVENT_STALLED:
#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();
}
}
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);
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
}
chThdSleepMilliseconds(15);
USB0->CTL &= ~USBx_CTL_RESUME;
#endif /* KINETIS_USB_USE_USB0 */
-#elif defined(STM32F0XX) /* K20x || KL2x */
+#elif defined(STM32F0XX) || defined(STM32F1XX) /* K20x || KL2x */
STM32_USB->CNTR |= CNTR_RESUME;
chThdSleepMilliseconds(15);
STM32_USB->CNTR &= ~CNTR_RESUME;
-#else /* STM32F0XX */
+#else /* STM32F0XX || STM32F1XX */
#warning Sending remote wakeup packet not implemented for your platform.
#endif /* K20x || KL2x */
}
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
#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;
* 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();
}
.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();
}
#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_EPSIZE) {
- osalSysUnlockFromISR();
- usbPrepareQueuedTransmit(usbp, CONSOLE_ENDPOINT, &console_queue, CONSOLE_EPSIZE);
- 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();
/* 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_EPSIZE)) {
- osalSysUnlock();
- usbPrepareQueuedTransmit(usbp, CONSOLE_ENDPOINT, &console_queue, CONSOLE_EPSIZE);
- 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);
+ }
}
}
* 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_EPSIZE]; /* 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_EPSIZE)) {
+ /* 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_EPSIZE; i++)
- buf[i] = 0;
- osalSysUnlockFromISR();
- usbPrepareTransmit(usbp, CONSOLE_ENDPOINT, buf, CONSOLE_EPSIZE);
- 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();
}
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 */