summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorfredizzimo <fsundvik@gmail.com>2018-04-16 03:42:53 +0300
committerJack Humbert <jack.humb@gmail.com>2018-04-15 20:42:53 -0400
commite9d32b60b7f103cda42a19c5216e65b7b64ce9eb (patch)
treea954db818d58a86ee5bb4189568de82a453b11e8
parente2fb3079c7168ba109dfeeec20931ad75870186a (diff)
downloadqmk_firmware-e9d32b60b7f103cda42a19c5216e65b7b64ce9eb.tar.gz
qmk_firmware-e9d32b60b7f103cda42a19c5216e65b7b64ce9eb.zip
Add a custom USB driver for ARM (#2750)
* Copy Chibios serial_usb_driver into the chibios/protocol It's renamed to usb_driver to avoid name conflicts * Make the usb driver compile * Disable ChibiOS serial usb driver for all keyboards * Change usb_main to use QMKUSBDriver * Initialize the usb driver buffers * Add support for fixed size queues * Fix USB driver initialization * Don't transfer an empty packet for fixed size streams
-rw-r--r--keyboards/chibios_test/stm32_f072_onekey/halconf.h2
-rw-r--r--keyboards/chibios_test/stm32_f103_onekey/halconf.h2
-rw-r--r--keyboards/chibios_test/teensy_lc_onekey/halconf.h2
-rw-r--r--keyboards/clueboard/60/halconf.h2
-rw-r--r--keyboards/ergodox_infinity/halconf.h2
-rw-r--r--keyboards/infinity60/halconf.h2
-rw-r--r--keyboards/jm60/halconf.h2
-rw-r--r--keyboards/k_type/halconf.h2
-rw-r--r--keyboards/whitefox/halconf.h2
-rw-r--r--tmk_core/protocol/chibios.mk1
-rw-r--r--tmk_core/protocol/chibios/usb_driver.c502
-rw-r--r--tmk_core/protocol/chibios/usb_driver.h184
-rw-r--r--tmk_core/protocol/chibios/usb_main.c84
13 files changed, 738 insertions, 51 deletions
diff --git a/keyboards/chibios_test/stm32_f072_onekey/halconf.h b/keyboards/chibios_test/stm32_f072_onekey/halconf.h
index 7625725585..8b9724b1a3 100644
--- a/keyboards/chibios_test/stm32_f072_onekey/halconf.h
+++ b/keyboards/chibios_test/stm32_f072_onekey/halconf.h
@@ -139,7 +139,7 @@
* @brief Enables the SERIAL over USB subsystem.
*/
#if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__)
-#define HAL_USE_SERIAL_USB TRUE
+#define HAL_USE_SERIAL_USB FALSE
#endif
/**
diff --git a/keyboards/chibios_test/stm32_f103_onekey/halconf.h b/keyboards/chibios_test/stm32_f103_onekey/halconf.h
index 7625725585..8b9724b1a3 100644
--- a/keyboards/chibios_test/stm32_f103_onekey/halconf.h
+++ b/keyboards/chibios_test/stm32_f103_onekey/halconf.h
@@ -139,7 +139,7 @@
* @brief Enables the SERIAL over USB subsystem.
*/
#if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__)
-#define HAL_USE_SERIAL_USB TRUE
+#define HAL_USE_SERIAL_USB FALSE
#endif
/**
diff --git a/keyboards/chibios_test/teensy_lc_onekey/halconf.h b/keyboards/chibios_test/teensy_lc_onekey/halconf.h
index 5e1f6a8a14..1b6f2adc20 100644
--- a/keyboards/chibios_test/teensy_lc_onekey/halconf.h
+++ b/keyboards/chibios_test/teensy_lc_onekey/halconf.h
@@ -139,7 +139,7 @@
* @brief Enables the SERIAL over USB subsystem.
*/
#if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__)
-#define HAL_USE_SERIAL_USB TRUE
+#define HAL_USE_SERIAL_USB FALSE
#endif
/**
diff --git a/keyboards/clueboard/60/halconf.h b/keyboards/clueboard/60/halconf.h
index 8fe8e0c6f5..e617fdffc7 100644
--- a/keyboards/clueboard/60/halconf.h
+++ b/keyboards/clueboard/60/halconf.h
@@ -146,7 +146,7 @@
* @brief Enables the SERIAL over USB subsystem.
*/
#if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__)
-#define HAL_USE_SERIAL_USB TRUE
+#define HAL_USE_SERIAL_USB FALSE
#endif
/**
diff --git a/keyboards/ergodox_infinity/halconf.h b/keyboards/ergodox_infinity/halconf.h
index 3f4ffb7bef..ade55ae72d 100644
--- a/keyboards/ergodox_infinity/halconf.h
+++ b/keyboards/ergodox_infinity/halconf.h
@@ -139,7 +139,7 @@
* @brief Enables the SERIAL over USB subsystem.
*/
#if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__)
-#define HAL_USE_SERIAL_USB TRUE
+#define HAL_USE_SERIAL_USB FALSE
#endif
/**
diff --git a/keyboards/infinity60/halconf.h b/keyboards/infinity60/halconf.h
index f48413c6dd..b87b0635c4 100644
--- a/keyboards/infinity60/halconf.h
+++ b/keyboards/infinity60/halconf.h
@@ -139,7 +139,7 @@
* @brief Enables the SERIAL over USB subsystem.
*/
#if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__)
-#define HAL_USE_SERIAL_USB TRUE
+#define HAL_USE_SERIAL_USB FALSE
#endif
/**
diff --git a/keyboards/jm60/halconf.h b/keyboards/jm60/halconf.h
index 7625725585..8b9724b1a3 100644
--- a/keyboards/jm60/halconf.h
+++ b/keyboards/jm60/halconf.h
@@ -139,7 +139,7 @@
* @brief Enables the SERIAL over USB subsystem.
*/
#if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__)
-#define HAL_USE_SERIAL_USB TRUE
+#define HAL_USE_SERIAL_USB FALSE
#endif
/**
diff --git a/keyboards/k_type/halconf.h b/keyboards/k_type/halconf.h
index f48413c6dd..b87b0635c4 100644
--- a/keyboards/k_type/halconf.h
+++ b/keyboards/k_type/halconf.h
@@ -139,7 +139,7 @@
* @brief Enables the SERIAL over USB subsystem.
*/
#if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__)
-#define HAL_USE_SERIAL_USB TRUE
+#define HAL_USE_SERIAL_USB FALSE
#endif
/**
diff --git a/keyboards/whitefox/halconf.h b/keyboards/whitefox/halconf.h
index f48413c6dd..b87b0635c4 100644
--- a/keyboards/whitefox/halconf.h
+++ b/keyboards/whitefox/halconf.h
@@ -139,7 +139,7 @@
* @brief Enables the SERIAL over USB subsystem.
*/
#if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__)
-#define HAL_USE_SERIAL_USB TRUE
+#define HAL_USE_SERIAL_USB FALSE
#endif
/**
diff --git a/tmk_core/protocol/chibios.mk b/tmk_core/protocol/chibios.mk
index 6e7cfbd832..222fb4dad4 100644
--- a/tmk_core/protocol/chibios.mk
+++ b/tmk_core/protocol/chibios.mk
@@ -5,6 +5,7 @@ CHIBIOS_DIR = $(PROTOCOL_DIR)/chibios
SRC += $(CHIBIOS_DIR)/usb_main.c
SRC += $(CHIBIOS_DIR)/main.c
SRC += usb_descriptor.c
+SRC += $(CHIBIOS_DIR)/usb_driver.c
VPATH += $(TMK_PATH)/$(PROTOCOL_DIR)
VPATH += $(TMK_PATH)/$(CHIBIOS_DIR)
diff --git a/tmk_core/protocol/chibios/usb_driver.c b/tmk_core/protocol/chibios/usb_driver.c
new file mode 100644
index 0000000000..fe535eeb3a
--- /dev/null
+++ b/tmk_core/protocol/chibios/usb_driver.c
@@ -0,0 +1,502 @@
+/*
+ ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+/**
+ * @file hal_serial_usb.c
+ * @brief Serial over USB Driver code.
+ *
+ * @addtogroup SERIAL_USB
+ * @{
+ */
+
+#include "hal.h"
+#include "usb_driver.h"
+#include <string.h>
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local variables and types. */
+/*===========================================================================*/
+
+/*
+ * Current Line Coding.
+ */
+static cdc_linecoding_t linecoding = {
+ {0x00, 0x96, 0x00, 0x00}, /* 38400. */
+ LC_STOP_1, LC_PARITY_NONE, 8
+};
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+static bool qmkusb_start_receive(QMKUSBDriver *qmkusbp) {
+ uint8_t *buf;
+
+ /* If the USB driver is not in the appropriate state then transactions
+ must not be started.*/
+ if ((usbGetDriverStateI(qmkusbp->config->usbp) != USB_ACTIVE) ||
+ (qmkusbp->state != QMKUSB_READY)) {
+ return true;
+ }
+
+ /* Checking if there is already a transaction ongoing on the endpoint.*/
+ if (usbGetReceiveStatusI(qmkusbp->config->usbp, qmkusbp->config->bulk_in)) {
+ return true;
+ }
+
+ /* Checking if there is a buffer ready for incoming data.*/
+ buf = ibqGetEmptyBufferI(&qmkusbp->ibqueue);
+ if (buf == NULL) {
+ return true;
+ }
+
+ /* Buffer found, starting a new transaction.*/
+ usbStartReceiveI(qmkusbp->config->usbp, qmkusbp->config->bulk_out,
+ buf, qmkusbp->ibqueue.bsize - sizeof(size_t));
+
+ return false;
+}
+
+/*
+ * Interface implementation.
+ */
+
+static size_t _write(void *ip, const uint8_t *bp, size_t n) {
+
+ return obqWriteTimeout(&((QMKUSBDriver *)ip)->obqueue, bp,
+ n, TIME_INFINITE);
+}
+
+static size_t _read(void *ip, uint8_t *bp, size_t n) {
+
+ return ibqReadTimeout(&((QMKUSBDriver *)ip)->ibqueue, bp,
+ n, TIME_INFINITE);
+}
+
+static msg_t _put(void *ip, uint8_t b) {
+
+ return obqPutTimeout(&((QMKUSBDriver *)ip)->obqueue, b, TIME_INFINITE);
+}
+
+static msg_t _get(void *ip) {
+
+ return ibqGetTimeout(&((QMKUSBDriver *)ip)->ibqueue, TIME_INFINITE);
+}
+
+static msg_t _putt(void *ip, uint8_t b, systime_t timeout) {
+
+ return obqPutTimeout(&((QMKUSBDriver *)ip)->obqueue, b, timeout);
+}
+
+static msg_t _gett(void *ip, systime_t timeout) {
+
+ return ibqGetTimeout(&((QMKUSBDriver *)ip)->ibqueue, timeout);
+}
+
+static size_t _writet(void *ip, const uint8_t *bp, size_t n, systime_t timeout) {
+
+ return obqWriteTimeout(&((QMKUSBDriver *)ip)->obqueue, bp, n, timeout);
+}
+
+static size_t _readt(void *ip, uint8_t *bp, size_t n, systime_t timeout) {
+
+ return ibqReadTimeout(&((QMKUSBDriver *)ip)->ibqueue, bp, n, timeout);
+}
+
+static const struct QMKUSBDriverVMT vmt = {
+ _write, _read, _put, _get,
+ _putt, _gett, _writet, _readt
+};
+
+/**
+ * @brief Notification of empty buffer released into the input buffers queue.
+ *
+ * @param[in] bqp the buffers queue pointer.
+ */
+static void ibnotify(io_buffers_queue_t *bqp) {
+ QMKUSBDriver *qmkusbp = bqGetLinkX(bqp);
+ (void) qmkusb_start_receive(qmkusbp);
+}
+
+/**
+ * @brief Notification of filled buffer inserted into the output buffers queue.
+ *
+ * @param[in] bqp the buffers queue pointer.
+ */
+static void obnotify(io_buffers_queue_t *bqp) {
+ size_t n;
+ QMKUSBDriver *qmkusbp = bqGetLinkX(bqp);
+
+ /* If the USB driver is not in the appropriate state then transactions
+ must not be started.*/
+ if ((usbGetDriverStateI(qmkusbp->config->usbp) != USB_ACTIVE) ||
+ (qmkusbp->state != QMKUSB_READY)) {
+ return;
+ }
+
+ /* Checking if there is already a transaction ongoing on the endpoint.*/
+ if (!usbGetTransmitStatusI(qmkusbp->config->usbp, qmkusbp->config->bulk_in)) {
+ /* Trying to get a full buffer.*/
+ uint8_t *buf = obqGetFullBufferI(&qmkusbp->obqueue, &n);
+ if (buf != NULL) {
+ /* Buffer found, starting a new transaction.*/
+ usbStartTransmitI(qmkusbp->config->usbp, qmkusbp->config->bulk_in, buf, n);
+ }
+ }
+}
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Serial Driver initialization.
+ * @note This function is implicitly invoked by @p halInit(), there is
+ * no need to explicitly initialize the driver.
+ *
+ * @init
+ */
+void qmkusbInit(void) {
+}
+
+/**
+ * @brief Initializes a generic full duplex driver object.
+ * @details The HW dependent part of the initialization has to be performed
+ * outside, usually in the hardware initialization code.
+ *
+ * @param[out] qmkusbp pointer to a @p QMKUSBDriver structure
+ *
+ * @init
+ */
+void qmkusbObjectInit(QMKUSBDriver *qmkusbp, const QMKUSBConfig *config) {
+
+ qmkusbp->vmt = &vmt;
+ osalEventObjectInit(&qmkusbp->event);
+ qmkusbp->state = QMKUSB_STOP;
+ // Note that the config uses the USB direction naming
+ ibqObjectInit(&qmkusbp->ibqueue, true, config->ob,
+ config->out_size, config->out_buffers,
+ ibnotify, qmkusbp);
+ obqObjectInit(&qmkusbp->obqueue, true, config->ib,
+ config->in_size, config->in_buffers,
+ obnotify, qmkusbp);
+}
+
+/**
+ * @brief Configures and starts the driver.
+ *
+ * @param[in] qmkusbp pointer to a @p QMKUSBDriver object
+ * @param[in] config the serial over USB driver configuration
+ *
+ * @api
+ */
+void qmkusbStart(QMKUSBDriver *qmkusbp, const QMKUSBConfig *config) {
+ USBDriver *usbp = config->usbp;
+
+ osalDbgCheck(qmkusbp != NULL);
+
+ osalSysLock();
+ osalDbgAssert((qmkusbp->state == QMKUSB_STOP) || (qmkusbp->state == QMKUSB_READY),
+ "invalid state");
+ usbp->in_params[config->bulk_in - 1U] = qmkusbp;
+ usbp->out_params[config->bulk_out - 1U] = qmkusbp;
+ if (config->int_in > 0U) {
+ usbp->in_params[config->int_in - 1U] = qmkusbp;
+ }
+ qmkusbp->config = config;
+ qmkusbp->state = QMKUSB_READY;
+ osalSysUnlock();
+}
+
+/**
+ * @brief Stops the driver.
+ * @details Any thread waiting on the driver's queues will be awakened with
+ * the message @p MSG_RESET.
+ *
+ * @param[in] qmkusbp pointer to a @p QMKUSBDriver object
+ *
+ * @api
+ */
+void qmkusbStop(QMKUSBDriver *qmkusbp) {
+ USBDriver *usbp = qmkusbp->config->usbp;
+
+ osalDbgCheck(qmkusbp != NULL);
+
+ osalSysLock();
+
+ osalDbgAssert((qmkusbp->state == QMKUSB_STOP) || (qmkusbp->state == QMKUSB_READY),
+ "invalid state");
+
+ /* Driver in stopped state.*/
+ usbp->in_params[qmkusbp->config->bulk_in - 1U] = NULL;
+ usbp->out_params[qmkusbp->config->bulk_out - 1U] = NULL;
+ if (qmkusbp->config->int_in > 0U) {
+ usbp->in_params[qmkusbp->config->int_in - 1U] = NULL;
+ }
+ qmkusbp->config = NULL;
+ qmkusbp->state = QMKUSB_STOP;
+
+ /* Enforces a disconnection.*/
+ chnAddFlagsI(qmkusbp, CHN_DISCONNECTED);
+ ibqResetI(&qmkusbp->ibqueue);
+ obqResetI(&qmkusbp->obqueue);
+ osalOsRescheduleS();
+
+ osalSysUnlock();
+}
+
+/**
+ * @brief USB device suspend handler.
+ * @details Generates a @p CHN_DISCONNECT event and puts queues in
+ * non-blocking mode, this way the application cannot get stuck
+ * in the middle of an I/O operations.
+ * @note If this function is not called from an ISR then an explicit call
+ * to @p osalOsRescheduleS() in necessary afterward.
+ *
+ * @param[in] qmkusbp pointer to a @p QMKUSBDriver object
+ *
+ * @iclass
+ */
+void qmkusbSuspendHookI(QMKUSBDriver *qmkusbp) {
+
+ chnAddFlagsI(qmkusbp, CHN_DISCONNECTED);
+ bqSuspendI(&qmkusbp->ibqueue);
+ bqSuspendI(&qmkusbp->obqueue);
+}
+
+/**
+ * @brief USB device wakeup handler.
+ * @details Generates a @p CHN_CONNECT event and resumes normal queues
+ * operations.
+ *
+ * @note If this function is not called from an ISR then an explicit call
+ * to @p osalOsRescheduleS() in necessary afterward.
+ *
+ * @param[in] qmkusbp pointer to a @p QMKUSBDriver object
+ *
+ * @iclass
+ */
+void qmkusbWakeupHookI(QMKUSBDriver *qmkusbp) {
+
+ chnAddFlagsI(qmkusbp, CHN_CONNECTED);
+ bqResumeX(&qmkusbp->ibqueue);
+ bqResumeX(&qmkusbp->obqueue);
+}
+
+/**
+ * @brief USB device configured handler.
+ *
+ * @param[in] qmkusbp pointer to a @p QMKUSBDriver object
+ *
+ * @iclass
+ */
+void qmkusbConfigureHookI(QMKUSBDriver *qmkusbp) {
+
+ ibqResetI(&qmkusbp->ibqueue);
+ bqResumeX(&qmkusbp->ibqueue);
+ obqResetI(&qmkusbp->obqueue);
+ bqResumeX(&qmkusbp->obqueue);
+ chnAddFlagsI(qmkusbp, CHN_CONNECTED);
+ (void) qmkusb_start_receive(qmkusbp);
+}
+
+/**
+ * @brief Default requests hook.
+ * @details Applications wanting to use the Serial over USB driver can use
+ * this function as requests hook in the USB configuration.
+ * The following requests are emulated:
+ * - CDC_GET_LINE_CODING.
+ * - CDC_SET_LINE_CODING.
+ * - CDC_SET_CONTROL_LINE_STATE.
+ * .
+ *
+ * @param[in] usbp pointer to the @p USBDriver object
+ * @return The hook status.
+ * @retval true Message handled internally.
+ * @retval false Message not handled.
+ */
+bool qmkusbRequestsHook(USBDriver *usbp) {
+
+ if ((usbp->setup[0] & USB_RTYPE_TYPE_MASK) == USB_RTYPE_TYPE_CLASS) {
+ switch (usbp->setup[1]) {
+ case CDC_GET_LINE_CODING:
+ usbSetupTransfer(usbp, (uint8_t *)&linecoding, sizeof(linecoding), NULL);
+ return true;
+ case CDC_SET_LINE_CODING:
+ usbSetupTransfer(usbp, (uint8_t *)&linecoding, sizeof(linecoding), NULL);
+ return true;
+ case CDC_SET_CONTROL_LINE_STATE:
+ /* Nothing to do, there are no control lines.*/
+ usbSetupTransfer(usbp, NULL, 0, NULL);
+ return true;
+ default:
+ return false;
+ }
+ }
+ return false;
+}
+
+/**
+ * @brief SOF handler.
+ * @details The SOF interrupt is used for automatic flushing of incomplete
+ * buffers pending in the output queue.
+ *
+ * @param[in] qmkusbp pointer to a @p QMKUSBDriver object
+ *
+ * @iclass
+ */
+void qmkusbSOFHookI(QMKUSBDriver *qmkusbp) {
+
+ /* If the USB driver is not in the appropriate state then transactions
+ must not be started.*/
+ if ((usbGetDriverStateI(qmkusbp->config->usbp) != USB_ACTIVE) ||
+ (qmkusbp->state != QMKUSB_READY)) {
+ return;
+ }
+
+ /* If there is already a transaction ongoing then another one cannot be
+ started.*/
+ if (usbGetTransmitStatusI(qmkusbp->config->usbp, qmkusbp->config->bulk_in)) {
+ return;
+ }
+
+ /* Checking if there only a buffer partially filled, if so then it is
+ enforced in the queue and transmitted.*/
+ if (obqTryFlushI(&qmkusbp->obqueue)) {
+ size_t n;
+ uint8_t *buf = obqGetFullBufferI(&qmkusbp->obqueue, &n);
+
+ /* For fixed size drivers, fill the end with zeros */
+ if (qmkusbp->config->fixed_size) {
+ memset(buf + n, 0, qmkusbp->config->in_size - n);
+ n = qmkusbp->config->in_size;
+ }
+
+ osalDbgAssert(buf != NULL, "queue is empty");
+
+ usbStartTransmitI(qmkusbp->config->usbp, qmkusbp->config->bulk_in, buf, n);
+ }
+}
+
+/**
+ * @brief Default data transmitted callback.
+ * @details The application must use this function as callback for the IN
+ * data endpoint.
+ *
+ * @param[in] usbp pointer to the @p USBDriver object
+ * @param[in] ep IN endpoint number
+ */
+void qmkusbDataTransmitted(USBDriver *usbp, usbep_t ep) {
+ uint8_t *buf;
+ size_t n;
+ QMKUSBDriver *qmkusbp = usbp->in_params[ep - 1U];
+
+ if (qmkusbp == NULL) {
+ return;
+ }
+
+ osalSysLockFromISR();
+
+ /* Signaling that space is available in the output queue.*/
+ chnAddFlagsI(qmkusbp, CHN_OUTPUT_EMPTY);
+
+ /* Freeing the buffer just transmitted, if it was not a zero size packet.*/
+ if (usbp->epc[ep]->in_state->txsize > 0U) {
+ obqReleaseEmptyBufferI(&qmkusbp->obqueue);
+ }
+
+ /* Checking if there is a buffer ready for transmission.*/
+ buf = obqGetFullBufferI(&qmkusbp->obqueue, &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.*/
+ usbStartTransmitI(usbp, ep, buf, n);
+ }
+ else if ((usbp->epc[ep]->in_state->txsize > 0U) &&
+ ((usbp->epc[ep]->in_state->txsize &
+ ((size_t)usbp->epc[ep]->in_maxsize - 1U)) == 0U)) {
+ /* Transmit zero sized packet in case the last one has maximum allowed
+ size. Otherwise the recipient may expect more data coming soon and
+ not return buffered data to app. See section 5.8.3 Bulk Transfer
+ Packet Size Constraints of the USB Specification document.*/
+ if (!qmkusbp->config->fixed_size) {
+ usbStartTransmitI(usbp, ep, usbp->setup, 0);
+ }
+
+ }
+ else {
+ /* Nothing to transmit.*/
+ }
+
+ osalSysUnlockFromISR();
+}
+
+/**
+ * @brief Default data received callback.
+ * @details The application must use this function as callback for the OUT
+ * data endpoint.
+ *
+ * @param[in] usbp pointer to the @p USBDriver object
+ * @param[in] ep OUT endpoint number
+ */
+void qmkusbDataReceived(USBDriver *usbp, usbep_t ep) {
+ QMKUSBDriver *qmkusbp = usbp->out_params[ep - 1U];
+ if (qmkusbp == NULL) {
+ return;
+ }
+
+ osalSysLockFromISR();
+
+ /* Signaling that data is available in the input queue.*/
+ chnAddFlagsI(qmkusbp, CHN_INPUT_AVAILABLE);
+
+ /* Posting the filled buffer in the queue.*/
+ ibqPostFullBufferI(&qmkusbp->ibqueue,
+ usbGetReceiveTransactionSizeX(qmkusbp->config->usbp,
+ qmkusbp->config->bulk_out));
+
+ /* The endpoint cannot be busy, we are in the context of the callback,
+ so a packet is in the buffer for sure. Trying to get a free buffer
+ for the next transaction.*/
+ (void) qmkusb_start_receive(qmkusbp);
+
+ osalSysUnlockFromISR();
+}
+
+/**
+ * @brief Default data received callback.
+ * @details The application must use this function as callback for the IN
+ * interrupt endpoint.
+ *
+ * @param[in] usbp pointer to the @p USBDriver object
+ * @param[in] ep endpoint number
+ */
+void qmkusbInterruptTransmitted(USBDriver *usbp, usbep_t ep) {
+
+ (void)usbp;
+ (void)ep;
+}
+
+/** @} */
diff --git a/tmk_core/protocol/chibios/usb_driver.h b/tmk_core/protocol/chibios/usb_driver.h
new file mode 100644
index 0000000000..558479e197
--- /dev/null
+++ b/tmk_core/protocol/chibios/usb_driver.h
@@ -0,0 +1,184 @@
+/*
+ ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+/**
+ * @file usb_driver.h
+ * @brief Usb driver suitable for both packet and serial formats
+ *
+ * @addtogroup SERIAL_USB
+ * @{
+ */
+
+#ifndef USB_DRIVER_H
+#define USB_DRIVER_H
+
+#include "hal_usb_cdc.h"
+
+/*===========================================================================*/
+/* Driver constants. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Derived constants and error checks. */
+/*===========================================================================*/
+
+#if HAL_USE_USB == FALSE
+#error "The USB Driver requires HAL_USE_USB"
+#endif
+
+/*===========================================================================*/
+/* Driver data structures and types. */
+/*===========================================================================*/
+
+/**
+ * @brief Driver state machine possible states.
+ */
+typedef enum {
+ QMKUSB_UNINIT = 0, /**< Not initialized. */
+ QMKUSB_STOP = 1, /**< Stopped. */
+ QMKUSB_READY = 2 /**< Ready. */
+} qmkusbstate_t;
+
+/**
+ * @brief Structure representing a serial over USB driver.
+ */
+typedef struct QMKUSBDriver QMKUSBDriver;
+
+/**
+ * @brief Serial over USB Driver configuration structure.
+ * @details An instance of this structure must be passed to @p sduStart()
+ * in order to configure and start the driver operations.
+ */
+typedef struct {
+ /**
+ * @brief USB driver to use.
+ */
+ USBDriver *usbp;
+ /**
+ * @brief Bulk IN endpoint used for outgoing data transfer.
+ */
+ usbep_t bulk_in;
+ /**
+ * @brief Bulk OUT endpoint used for incoming data transfer.
+ */
+ usbep_t bulk_out;
+ /**
+ * @brief Interrupt IN endpoint used for notifications.
+ * @note If set to zero then the INT endpoint is assumed to be not
+ * present, USB descriptors must be changed accordingly.
+ */
+ usbep_t int_in;
+
+ /**
+ * @brief The number of buffers in the queues
+ */
+ size_t in_buffers;
+ size_t out_buffers;
+
+ /**
+ * @brief The size of each buffer in the queue, typically the same as the endpoint size
+ */
+ size_t in_size;
+ size_t out_size;
+
+ /**
+ * @brief Always send full buffers in_size (the rest is filled with zeroes)
+ */
+ bool fixed_size;
+
+ /* Input buffer
+ * @note needs to be initialized with a memory buffer of the right size
+ */
+ uint8_t* ib;
+ /* Output buffer
+ * @note needs to be initialized with a memory buffer of the right size
+ */
+ uint8_t* ob;
+} QMKUSBConfig;
+
+/**
+ * @brief @p SerialDriver specific data.
+ */
+#define _qmk_usb_driver_data \
+ _base_asynchronous_channel_data \
+ /* Driver state.*/ \
+ qmkusbstate_t state; \
+ /* Input buffers queue.*/ \
+ input_buffers_queue_t ibqueue; \
+ /* Output queue.*/ \
+ output_buffers_queue_t obqueue; \
+ /* End of the mandatory fields.*/ \
+ /* Current configuration data.*/ \
+ const QMKUSBConfig *config;
+
+/**
+ * @brief @p SerialUSBDriver specific methods.
+ */
+#define _qmk_usb_driver_methods \
+ _base_asynchronous_channel_methods
+
+/**
+ * @extends BaseAsynchronousChannelVMT
+ *
+ * @brief @p SerialDriver virtual methods table.
+ */
+struct QMKUSBDriverVMT {
+ _qmk_usb_driver_methods
+};
+
+/**
+ * @extends BaseAsynchronousChannel
+ *
+ * @brief Full duplex serial driver class.
+ * @details This class extends @p BaseAsynchronousChannel by adding physical
+ * I/O queues.
+ */
+struct QMKUSBDriver {
+ /** @brief Virtual Methods Table.*/
+ const struct QMKUSBDriverVMT *vmt;
+ _qmk_usb_driver_data
+};
+
+/*===========================================================================*/
+/* Driver macros. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void qmkusbInit(void);
+ void qmkusbObjectInit(QMKUSBDriver *qmkusbp, const QMKUSBConfig * config);
+ void qmkusbStart(QMKUSBDriver *qmkusbp, const QMKUSBConfig *config);
+ void qmkusbStop(QMKUSBDriver *qmkusbp);
+ void qmkusbSuspendHookI(QMKUSBDriver *qmkusbp);
+ void qmkusbWakeupHookI(QMKUSBDriver *qmkusbp);
+ void qmkusbConfigureHookI(QMKUSBDriver *qmkusbp);
+ bool qmkusbRequestsHook(USBDriver *usbp);
+ void qmkusbSOFHookI(QMKUSBDriver *qmkusbp);
+ void qmkusbDataTransmitted(USBDriver *usbp, usbep_t ep);
+ void qmkusbDataReceived(USBDriver *usbp, usbep_t ep);
+ void qmkusbInterruptTransmitted(USBDriver *usbp, usbep_t ep);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USB_DRIVER_H */
+
+/** @} */
diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c
index f980024ab8..cbe2571944 100644
--- a/tmk_core/protocol/chibios/usb_main.c
+++ b/tmk_core/protocol/chibios/usb_main.c
@@ -29,6 +29,7 @@
#endif
#include "wait.h"
#include "usb_descriptor.h"
+#include "usb_driver.h"
#ifdef NKRO_ENABLE
#include "keycode_config.h"
@@ -170,27 +171,23 @@ static const USBEndpointConfig nkro_ep_config = {
typedef struct {
size_t queue_capacity_in;
size_t queue_capacity_out;
- uint8_t* queue_buffer_in;
- uint8_t* queue_buffer_out;
USBInEndpointState in_ep_state;
USBOutEndpointState out_ep_state;
USBInEndpointState int_ep_state;
USBEndpointConfig in_ep_config;
USBEndpointConfig out_ep_config;
USBEndpointConfig int_ep_config;
- const SerialUSBConfig config;
- SerialUSBDriver driver;
-} stream_driver_t;
+ const QMKUSBConfig config;
+ QMKUSBDriver driver;
+} usb_driver_config_t;
-#define STREAM_DRIVER(stream, notification) { \
+#define QMK_USB_DRIVER_CONFIG(stream, notification, fixedsize) { \
.queue_capacity_in = stream##_IN_CAPACITY, \
.queue_capacity_out = stream##_OUT_CAPACITY, \
- .queue_buffer_in = (uint8_t[BQ_BUFFER_SIZE(stream##_IN_CAPACITY, stream##_EPSIZE)]) {}, \
- .queue_buffer_out = (uint8_t[BQ_BUFFER_SIZE(stream##_OUT_CAPACITY,stream##_EPSIZE)]) {}, \
.in_ep_config = { \
.ep_mode = stream##_IN_MODE, \
.setup_cb = NULL, \
- .in_cb = sduDataTransmitted, \
+ .in_cb = qmkusbDataTransmitted, \
.out_cb = NULL, \
.in_maxsize = stream##_EPSIZE, \
.out_maxsize = 0, \
@@ -204,7 +201,7 @@ typedef struct {
.ep_mode = stream##_OUT_MODE, \
.setup_cb = NULL, \
.in_cb = NULL, \
- .out_cb = sduDataReceived, \
+ .out_cb = qmkusbDataReceived, \
.in_maxsize = 0, \
.out_maxsize = stream##_EPSIZE, \
/* The pointer to the states will be filled during initialization */ \
@@ -216,7 +213,7 @@ typedef struct {
.int_ep_config = { \
.ep_mode = USB_EP_MODE_TYPE_INTR, \
.setup_cb = NULL, \
- .in_cb = sduInterruptTransmitted, \
+ .in_cb = qmkusbInterruptTransmitted, \
.out_cb = NULL, \
.in_maxsize = CDC_NOTIFICATION_EPSIZE, \
.out_maxsize = 0, \
@@ -230,7 +227,14 @@ typedef struct {
.usbp = &USB_DRIVER, \
.bulk_in = stream##_IN_EPNUM, \
.bulk_out = stream##_OUT_EPNUM, \
- .int_in = notification \
+ .int_in = notification, \
+ .in_buffers = stream##_IN_CAPACITY, \
+ .out_buffers = stream##_OUT_CAPACITY, \
+ .in_size = stream##_EPSIZE, \
+ .out_size = stream##_EPSIZE, \
+ .fixed_size = fixedsize, \
+ .ib = (uint8_t[BQ_BUFFER_SIZE(stream##_IN_CAPACITY, stream##_EPSIZE)]) {}, \
+ .ob = (uint8_t[BQ_BUFFER_SIZE(stream##_OUT_CAPACITY,stream##_EPSIZE)]) {}, \
} \
}
@@ -238,36 +242,36 @@ typedef struct {
union {
struct {
#ifdef CONSOLE_ENABLE
- stream_driver_t console_driver;
+ usb_driver_config_t console_driver;
#endif
#ifdef RAW_ENABLE
- stream_driver_t raw_driver;
+ usb_driver_config_t raw_driver;
#endif
#ifdef MIDI_ENABLE
- stream_driver_t midi_driver;
+ usb_driver_config_t midi_driver;
#endif
#ifdef VIRTSER_ENABLE
- stream_driver_t serial_driver;
+ usb_driver_config_t serial_driver;
#endif
};
- stream_driver_t array[0];
+ usb_driver_config_t array[0];
};
-} stream_drivers_t;
+} usb_driver_configs_t;
-static stream_drivers_t drivers = {
+static usb_driver_configs_t drivers = {
#ifdef CONSOLE_ENABLE
#define CONSOLE_IN_CAPACITY 4
#define CONSOLE_OUT_CAPACITY 4
#define CONSOLE_IN_MODE USB_EP_MODE_TYPE_INTR
#define CONSOLE_OUT_MODE USB_EP_MODE_TYPE_INTR
- .console_driver = STREAM_DRIVER(CONSOLE, 0),
+ .console_driver = QMK_USB_DRIVER_CONFIG(CONSOLE, 0, true),
#endif
#ifdef RAW_ENABLE
#define RAW_IN_CAPACITY 4
#define RAW_OUT_CAPACITY 4
#define RAW_IN_MODE USB_EP_MODE_TYPE_INTR
#define RAW_OUT_MODE USB_EP_MODE_TYPE_INTR
- .raw_driver = STREAM_DRIVER(RAW, 0),
+ .raw_driver = QMK_USB_DRIVER_CONFIG(RAW, 0, false),
#endif
#ifdef MIDI_ENABLE
@@ -275,7 +279,7 @@ static stream_drivers_t drivers = {
#define MIDI_STREAM_OUT_CAPACITY 4
#define MIDI_STREAM_IN_MODE USB_EP_MODE_TYPE_BULK
#define MIDI_STREAM_OUT_MODE USB_EP_MODE_TYPE_BULK
- .midi_driver = STREAM_DRIVER(MIDI_STREAM, 0),
+ .midi_driver = QMK_USB_DRIVER_CONFIG(MIDI_STREAM, 0, false),
#endif
#ifdef VIRTSER_ENABLE
@@ -283,11 +287,11 @@ static stream_drivers_t drivers = {
#define CDC_OUT_CAPACITY 4
#define CDC_IN_MODE USB_EP_MODE_TYPE_BULK
#define CDC_OUT_MODE USB_EP_MODE_TYPE_BULK
- .serial_driver = STREAM_DRIVER(CDC, CDC_NOTIFICATION_EPNUM),
+ .serial_driver = QMK_USB_DRIVER_CONFIG(CDC, CDC_NOTIFICATION_EPNUM, false),
#endif
};
-#define NUM_STREAM_DRIVERS (sizeof(drivers) / sizeof(stream_driver_t))
+#define NUM_USB_DRIVERS (sizeof(drivers) / sizeof(usb_driver_config_t))
/* ---------------------------------------------------------
@@ -315,13 +319,13 @@ static void usb_event_cb(USBDriver *usbp, usbevent_t event) {
#ifdef NKRO_ENABLE
usbInitEndpointI(usbp, NKRO_IN_EPNUM, &nkro_ep_config);
#endif /* NKRO_ENABLE */
- for (int i=0;i<NUM_STREAM_DRIVERS;i++) {
+ for (int i=0;i<NUM_USB_DRIVERS;i++) {
usbInitEndpointI(usbp, drivers.array[i].config.bulk_in, &drivers.array[i].in_ep_config);
usbInitEndpointI(usbp, drivers.array[i].config.bulk_out, &drivers.array[i].out_ep_config);
if (drivers.array[i].config.int_in) {
usbInitEndpointI(usbp, drivers.array[i].config.int_in, &drivers.array[i].int_ep_config);
}
- sduConfigureHookI(&drivers.array[i].driver);
+ qmkusbConfigureHookI(&drivers.array[i].driver);
}
osalSysUnlockFromISR();
return;
@@ -333,20 +337,20 @@ static void usb_event_cb(USBDriver *usbp, usbevent_t event) {
case USB_EVENT_UNCONFIGURED:
/* Falls into.*/
case USB_EVENT_RESET:
- for (int i=0;i<NUM_STREAM_DRIVERS;i++) {
+ for (int i=0;i<NUM_USB_DRIVERS;i++) {
chSysLockFromISR();
/* Disconnection event on suspend.*/
- sduSuspendHookI(&drivers.array[i].driver);
+ qmkusbSuspendHookI(&drivers.array[i].driver);
chSysUnlockFromISR();
}
return;
case USB_EVENT_WAKEUP:
//TODO: from ISR! print("[W]");
- for (int i=0;i<NUM_STREAM_DRIVERS;i++) {
+ for (int i=0;i<NUM_USB_DRIVERS;i++) {
chSysLockFromISR();
/* Disconnection event on suspend.*/
- sduWakeupHookI(&drivers.array[i].driver);
+ qmkusbWakeupHookI(&drivers.array[i].driver);
chSysUnlockFromISR();
}
suspend_wakeup_init();
@@ -527,10 +531,10 @@ static bool usb_request_hook_cb(USBDriver *usbp) {
return TRUE;
}
- for (int i=0;i<NUM_STREAM_DRIVERS;i++) {
+ for (int i=0;i<NUM_USB_DRIVERS;i++) {
if (drivers.array[i].config.int_in) {
// NOTE: Assumes that we only have one serial driver
- return sduRequestsHook(usbp);
+ return qmkusbRequestsHook(usbp);
}
}
@@ -541,8 +545,8 @@ static bool usb_request_hook_cb(USBDriver *usbp) {
static void usb_sof_cb(USBDriver *usbp) {
kbd_sof_cb(usbp);
osalSysLockFromISR();
- for (int i=0; i<NUM_STREAM_DRIVERS;i++) {
- sduSOFHookI(&drivers.array[i].driver);
+ for (int i=0; i<NUM_USB_DRIVERS;i++) {
+ qmkusbSOFHookI(&drivers.array[i].driver);
}
osalSysUnlockFromISR();
}
@@ -560,17 +564,13 @@ static const USBConfig usbcfg = {
* Initialize the USB driver
*/
void init_usb_driver(USBDriver *usbp) {
- for (int i=0; i<NUM_STREAM_DRIVERS;i++) {
- SerialUSBDriver* driver = &drivers.array[i].driver;
+ for (int i=0; i<NUM_USB_DRIVERS;i++) {
+ QMKUSBDriver* driver = &drivers.array[i].driver;
drivers.array[i].in_ep_config.in_state = &drivers.array[i].in_ep_state;
drivers.array[i].out_ep_config.out_state = &drivers.array[i].out_ep_state;
drivers.array[i].int_ep_config.in_state = &drivers.array[i].int_ep_state;
- sduObjectInit(driver);
- bqnotify_t notify = driver->ibqueue.notify;
- ibqObjectInit(&driver->ibqueue, false, drivers.array[i].queue_buffer_in, drivers.array[i].in_ep_config.in_maxsize, drivers.array[i].queue_capacity_in, notify, driver);
- notify = driver->obqueue.notify;
- ibqObjectInit(&driver->ibqueue, false, drivers.array[i].queue_buffer_out, drivers.array[i].out_ep_config.out_maxsize, drivers.array[i].queue_capacity_out, notify, driver);
- sduStart(driver, &drivers.array[i].config);
+ qmkusbObjectInit(driver, &drivers.array[i].config);
+ qmkusbStart(driver, &drivers.array[i].config);
}
/*