X-Git-Url: https://git.friedersdorff.com/?a=blobdiff_plain;f=tmk_core%2Fprotocol%2Fchibios%2Fusb_main.c;h=60e1dd9451a8b382ef647da8c3fc8fc31b872016;hb=8345571e1df07cdbe612541baa9e4f3441ad3f9e;hp=382966b9a25ab2e7e34fdeef0c99d55fcbd40947;hpb=983d3be2da1231907aa735037b6b68717417b4a4;p=max%2Ftmk_keyboard.git diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c index 382966b9..60e1dd94 100644 --- a/tmk_core/protocol/chibios/usb_main.c +++ b/tmk_core/protocol/chibios/usb_main.c @@ -27,15 +27,34 @@ #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); @@ -52,11 +71,12 @@ 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 */ @@ -133,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), @@ -279,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) @@ -794,19 +814,13 @@ static void usb_event_cb(USBDriver *usbp, usbevent_t event) { 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: @@ -949,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(); } } @@ -966,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); @@ -1019,7 +1033,7 @@ 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 } @@ -1036,11 +1050,11 @@ void send_remote_wakeup(USBDriver *usbp) { 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 */ } @@ -1093,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 @@ -1123,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; @@ -1164,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(); } @@ -1202,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(); } @@ -1233,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_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(); @@ -1254,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_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); + } } } @@ -1273,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_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