diff --git a/tmk_core/protocol/chibios/chibios.c b/tmk_core/protocol/chibios/chibios.c
index 91bb252c7c..76a37ae538 100644
--- a/tmk_core/protocol/chibios/chibios.c
+++ b/tmk_core/protocol/chibios/chibios.c
@@ -192,15 +192,18 @@ void protocol_pre_task(void) {
             /* Remote wakeup */
             if ((USB_DRIVER.status & USB_GETSTATUS_REMOTE_WAKEUP_ENABLED) && suspend_wakeup_condition()) {
                 usbWakeupHost(&USB_DRIVER);
-                restart_usb_driver(&USB_DRIVER);
+#    if USB_SUSPEND_WAKEUP_DELAY > 0
+                // Some hubs, kvm switches, and monitors do
+                // weird things, with USB device state bouncing
+                // around wildly on wakeup, yielding race
+                // conditions that can corrupt the keyboard state.
+                //
+                // Pause for a while to let things settle...
+                wait_ms(USB_SUSPEND_WAKEUP_DELAY);
+#    endif
             }
         }
         /* Woken up */
-        // variables has been already cleared by the wakeup hook
-        send_keyboard_report();
-#    ifdef MOUSEKEY_ENABLE
-        mousekey_send();
-#    endif /* MOUSEKEY_ENABLE */
     }
 #endif
 }
@@ -218,4 +221,5 @@ void protocol_post_task(void) {
 #ifdef RAW_ENABLE
     raw_hid_task();
 #endif
+    usb_idle_task();
 }
diff --git a/tmk_core/protocol/chibios/chibios.mk b/tmk_core/protocol/chibios/chibios.mk
index 8eaf5b10d2..aee3f5f056 100644
--- a/tmk_core/protocol/chibios/chibios.mk
+++ b/tmk_core/protocol/chibios/chibios.mk
@@ -6,6 +6,8 @@ SRC += $(CHIBIOS_DIR)/usb_main.c
 SRC += $(CHIBIOS_DIR)/chibios.c
 SRC += usb_descriptor.c
 SRC += $(CHIBIOS_DIR)/usb_driver.c
+SRC += $(CHIBIOS_DIR)/usb_endpoints.c
+SRC += $(CHIBIOS_DIR)/usb_report_handling.c
 SRC += $(CHIBIOS_DIR)/usb_util.c
 SRC += $(LIBSRC)
 
diff --git a/tmk_core/protocol/chibios/usb_driver.c b/tmk_core/protocol/chibios/usb_driver.c
index ad45f9b1da..7c3ce44687 100644
--- a/tmk_core/protocol/chibios/usb_driver.c
+++ b/tmk_core/protocol/chibios/usb_driver.c
@@ -1,127 +1,51 @@
-/*
-    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
- * @{
- */
+// Copyright 2023 Stefan Kerkmann (@KarlK90)
+// Copyright 2021 Purdea Andrei
+// Copyright 2021 Michael Stapelberg
+// Copyright 2020 Ryan (@fauxpark)
+// Copyright 2016 Fredizzimo
+// Copyright 2016 Giovanni Di Sirio
+// SPDX-License-Identifier: GPL-3.0-or-later OR Apache-2.0
 
 #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};
+#include "usb_driver.h"
+#include "util.h"
 
 /*===========================================================================*/
 /* Driver local functions.                                                   */
 /*===========================================================================*/
 
-static bool qmkusb_start_receive(QMKUSBDriver *qmkusbp) {
-    uint8_t *buf;
-
+static void usb_start_receive(usb_endpoint_out_t *endpoint) {
     /* 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;
+    if ((usbGetDriverStateI(endpoint->config.usbp) != USB_ACTIVE)) {
+        return;
     }
 
     /* Checking if there is already a transaction ongoing on the endpoint.*/
-    if (usbGetReceiveStatusI(qmkusbp->config->usbp, qmkusbp->config->bulk_out)) {
-        return true;
+    if (usbGetReceiveStatusI(endpoint->config.usbp, endpoint->config.ep)) {
+        return;
     }
 
     /* Checking if there is a buffer ready for incoming data.*/
-    buf = ibqGetEmptyBufferI(&qmkusbp->ibqueue);
-    if (buf == NULL) {
-        return true;
+    uint8_t *buffer = ibqGetEmptyBufferI(&endpoint->ibqueue);
+    if (buffer == NULL) {
+        return;
     }
 
     /* Buffer found, starting a new transaction.*/
-    usbStartReceiveI(qmkusbp->config->usbp, qmkusbp->config->bulk_out, buf, qmkusbp->ibqueue.bsize - sizeof(size_t));
-
-    return false;
+    usbStartReceiveI(endpoint->config.usbp, endpoint->config.ep, buffer, endpoint->ibqueue.bsize - sizeof(size_t));
 }
 
-/*
- * 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, sysinterval_t timeout) {
-    return obqPutTimeout(&((QMKUSBDriver *)ip)->obqueue, b, timeout);
-}
-
-static msg_t _gett(void *ip, sysinterval_t timeout) {
-    return ibqGetTimeout(&((QMKUSBDriver *)ip)->ibqueue, timeout);
-}
-
-static size_t _writet(void *ip, const uint8_t *bp, size_t n, sysinterval_t timeout) {
-    return obqWriteTimeout(&((QMKUSBDriver *)ip)->obqueue, bp, n, timeout);
-}
-
-static size_t _readt(void *ip, uint8_t *bp, size_t n, sysinterval_t timeout) {
-    return ibqReadTimeout(&((QMKUSBDriver *)ip)->ibqueue, bp, n, timeout);
-}
-
-static const struct QMKUSBDriverVMT vmt = {0, _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);
+    usb_endpoint_out_t *endpoint = bqGetLinkX(bqp);
+    usb_start_receive(endpoint);
 }
 
 /**
@@ -130,22 +54,22 @@ static void ibnotify(io_buffers_queue_t *bqp) {
  * @param[in] bqp       the buffers queue pointer.
  */
 static void obnotify(io_buffers_queue_t *bqp) {
-    size_t        n;
-    QMKUSBDriver *qmkusbp = bqGetLinkX(bqp);
+    usb_endpoint_in_t *endpoint = bqGetLinkX(bqp);
 
-    /* If the USB driver is not in the appropriate state then transactions
+    /* If the USB endpoint is not in the appropriate state then transactions
        must not be started.*/
-    if ((usbGetDriverStateI(qmkusbp->config->usbp) != USB_ACTIVE) || (qmkusbp->state != QMKUSB_READY)) {
+    if ((usbGetDriverStateI(endpoint->config.usbp) != USB_ACTIVE)) {
         return;
     }
 
     /* Checking if there is already a transaction ongoing on the endpoint.*/
-    if (!usbGetTransmitStatusI(qmkusbp->config->usbp, qmkusbp->config->bulk_in)) {
+    if (!usbGetTransmitStatusI(endpoint->config.usbp, endpoint->config.ep)) {
         /* Trying to get a full buffer.*/
-        uint8_t *buf = obqGetFullBufferI(&qmkusbp->obqueue, &n);
-        if (buf != NULL) {
+        size_t   n;
+        uint8_t *buffer = obqGetFullBufferI(&endpoint->obqueue, &n);
+        if (buffer != NULL) {
             /* Buffer found, starting a new transaction.*/
-            usbStartTransmitI(qmkusbp->config->usbp, qmkusbp->config->bulk_in, buf, n);
+            usbStartTransmitI(endpoint->config.usbp, endpoint->config.ep, buffer, n);
         }
     }
 }
@@ -154,264 +78,149 @@ static void obnotify(io_buffers_queue_t *bqp) {
 /* 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) {}
+void usb_endpoint_in_init(usb_endpoint_in_t *endpoint) {
+    usb_endpoint_config_t *config = &endpoint->config;
+    endpoint->ep_config.in_state  = &endpoint->ep_in_state;
 
-/**
- * @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);
+#if defined(USB_ENDPOINTS_ARE_REORDERABLE)
+    if (endpoint->is_shared) {
+        endpoint->ep_config.out_state = &endpoint->ep_out_state;
+    }
+#endif
+    obqObjectInit(&endpoint->obqueue, true, config->buffer, config->buffer_size, config->buffer_capacity, obnotify, endpoint);
 }
 
-/**
- * @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;
+void usb_endpoint_out_init(usb_endpoint_out_t *endpoint) {
+    usb_endpoint_config_t *config = &endpoint->config;
+    endpoint->ep_config.out_state = &endpoint->ep_out_state;
+    ibqObjectInit(&endpoint->ibqueue, true, config->buffer, config->buffer_size, config->buffer_capacity, ibnotify, endpoint);
+}
 
-    osalDbgCheck(qmkusbp != NULL);
+void usb_endpoint_in_start(usb_endpoint_in_t *endpoint) {
+    osalDbgCheck(endpoint != 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;
+    osalDbgAssert((usbGetDriverStateI(endpoint->config.usbp) == USB_STOP) || (usbGetDriverStateI(endpoint->config.usbp) == USB_READY), "invalid state");
+    endpoint->config.usbp->in_params[endpoint->config.ep - 1U] = endpoint;
+    endpoint->timed_out                                        = false;
     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);
+void usb_endpoint_out_start(usb_endpoint_out_t *endpoint) {
+    osalDbgCheck(endpoint != NULL);
 
     osalSysLock();
+    osalDbgAssert((usbGetDriverStateI(endpoint->config.usbp) == USB_STOP) || (usbGetDriverStateI(endpoint->config.usbp) == USB_READY), "invalid state");
+    endpoint->config.usbp->out_params[endpoint->config.ep - 1U] = endpoint;
+    endpoint->timed_out                                         = false;
+    osalSysUnlock();
+}
 
-    osalDbgAssert((qmkusbp->state == QMKUSB_STOP) || (qmkusbp->state == QMKUSB_READY), "invalid state");
+void usb_endpoint_in_stop(usb_endpoint_in_t *endpoint) {
+    osalDbgCheck(endpoint != NULL);
 
-    /* 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;
+    osalSysLock();
+    endpoint->config.usbp->in_params[endpoint->config.ep - 1U] = NULL;
+
+    bqSuspendI(&endpoint->obqueue);
+    obqResetI(&endpoint->obqueue);
+    if (endpoint->report_storage != NULL) {
+        endpoint->report_storage->reset_report(endpoint->report_storage->reports);
     }
-    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);
+void usb_endpoint_out_stop(usb_endpoint_out_t *endpoint) {
+    osalDbgCheck(endpoint != NULL);
+
+    osalSysLock();
+    osalDbgAssert((usbGetDriverStateI(endpoint->config.usbp) == USB_STOP) || (usbGetDriverStateI(endpoint->config.usbp) == USB_READY), "invalid state");
+
+    bqSuspendI(&endpoint->ibqueue);
+    ibqResetI(&endpoint->ibqueue);
+    osalOsRescheduleS();
+    osalSysUnlock();
 }
 
-/**
- * @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);
-}
+void usb_endpoint_in_suspend_cb(usb_endpoint_in_t *endpoint) {
+    bqSuspendI(&endpoint->obqueue);
+    obqResetI(&endpoint->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);
+    if (endpoint->report_storage != NULL) {
+        endpoint->report_storage->reset_report(endpoint->report_storage->reports);
     }
 }
 
-/**
- * @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];
+void usb_endpoint_out_suspend_cb(usb_endpoint_out_t *endpoint) {
+    bqSuspendI(&endpoint->ibqueue);
+    ibqResetI(&endpoint->ibqueue);
+}
 
-    if (qmkusbp == NULL) {
+void usb_endpoint_in_wakeup_cb(usb_endpoint_in_t *endpoint) {
+    bqResumeX(&endpoint->obqueue);
+}
+
+void usb_endpoint_out_wakeup_cb(usb_endpoint_out_t *endpoint) {
+    bqResumeX(&endpoint->ibqueue);
+}
+
+void usb_endpoint_in_configure_cb(usb_endpoint_in_t *endpoint) {
+    usbInitEndpointI(endpoint->config.usbp, endpoint->config.ep, &endpoint->ep_config);
+    obqResetI(&endpoint->obqueue);
+    bqResumeX(&endpoint->obqueue);
+}
+
+void usb_endpoint_out_configure_cb(usb_endpoint_out_t *endpoint) {
+    /* The current assumption is that there are no standalone OUT endpoints,
+     * therefore if we share an endpoint with an IN endpoint, it is already
+     * initialized. */
+#if !defined(USB_ENDPOINTS_ARE_REORDERABLE)
+    usbInitEndpointI(endpoint->config.usbp, endpoint->config.ep, &endpoint->ep_config);
+#endif
+    ibqResetI(&endpoint->ibqueue);
+    bqResumeX(&endpoint->ibqueue);
+    (void)usb_start_receive(endpoint);
+}
+
+void usb_endpoint_in_tx_complete_cb(USBDriver *usbp, usbep_t ep) {
+    usb_endpoint_in_t *endpoint = usbp->in_params[ep - 1U];
+    size_t             n;
+    uint8_t *          buffer;
+
+    if (endpoint == NULL) {
         return;
     }
 
     osalSysLockFromISR();
 
-    /* Signaling that space is available in the output queue.*/
-    chnAddFlagsI(qmkusbp, CHN_OUTPUT_EMPTY);
+    /* Sending succeded, so we can reset the timed out state. */
+    endpoint->timed_out = false;
 
     /* Freeing the buffer just transmitted, if it was not a zero size packet.*/
-    if (usbp->epc[ep]->in_state->txsize > 0U) {
-        obqReleaseEmptyBufferI(&qmkusbp->obqueue);
+    if (!obqIsEmptyI(&endpoint->obqueue) && usbp->epc[ep]->in_state->txsize > 0U) {
+        /* Store the last send report in the endpoint to be retrieved by a
+         * GET_REPORT request or IDLE report handling. */
+        if (endpoint->report_storage != NULL) {
+            buffer = obqGetFullBufferI(&endpoint->obqueue, &n);
+            endpoint->report_storage->set_report(endpoint->report_storage->reports, buffer, n);
+        }
+        obqReleaseEmptyBufferI(&endpoint->obqueue);
     }
 
     /* Checking if there is a buffer ready for transmission.*/
-    buf = obqGetFullBufferI(&qmkusbp->obqueue, &n);
+    buffer = obqGetFullBufferI(&endpoint->obqueue, &n);
 
-    if (buf != NULL) {
+    if (buffer != 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)) {
+        usbStartTransmitI(usbp, ep, buffer, n);
+    } else if ((usbp->epc[ep]->ep_mode == USB_EP_MODE_TYPE_BULK) && (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);
-        }
-
+         * 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. */
+        usbStartTransmitI(usbp, ep, usbp->setup, 0);
     } else {
         /* Nothing to transmit.*/
     }
@@ -419,47 +228,114 @@ void qmkusbDataTransmitted(USBDriver *usbp, usbep_t ep) {
     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) {
+void usb_endpoint_out_rx_complete_cb(USBDriver *usbp, usbep_t ep) {
+    usb_endpoint_out_t *endpoint = usbp->out_params[ep - 1U];
+    if (endpoint == NULL) {
         return;
     }
 
     osalSysLockFromISR();
 
-    /* Signaling that data is available in the input queue.*/
-    chnAddFlagsI(qmkusbp, CHN_INPUT_AVAILABLE);
+    size_t size = usbGetReceiveTransactionSizeX(usbp, ep);
+    if (size > 0) {
+        /* Posting the filled buffer in the queue.*/
+        ibqPostFullBufferI(&endpoint->ibqueue, usbGetReceiveTransactionSizeX(endpoint->config.usbp, endpoint->config.ep));
+    }
 
-    /* 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);
+    /* 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.*/
+    usb_start_receive(endpoint);
 
     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;
+bool usb_endpoint_in_send(usb_endpoint_in_t *endpoint, const uint8_t *data, size_t size, sysinterval_t timeout, bool buffered) {
+    osalDbgCheck((endpoint != NULL) && (data != NULL) && (size > 0U) && (size <= endpoint->config.buffer_size));
+
+    osalSysLock();
+    if (usbGetDriverStateI(endpoint->config.usbp) != USB_ACTIVE) {
+        osalSysUnlock();
+        return false;
+    }
+
+    /* Short circuit the waiting if this endpoint timed out before, e.g. if
+     * nobody is listening on this endpoint (is disconnected) such as
+     * `hid_listen`/`qmk console` or we are in an environment with a very
+     * restricted USB stack. The reason is to not introduce micro lock-ups if
+     * the report is send periodically. */
+    if (endpoint->timed_out && timeout != TIME_INFINITE) {
+        timeout = TIME_IMMEDIATE;
+    }
+    osalSysUnlock();
+
+    while (true) {
+        size_t sent = obqWriteTimeout(&endpoint->obqueue, data, size, timeout);
+
+        if (sent < size) {
+            osalSysLock();
+            endpoint->timed_out |= sent == 0;
+            bqSuspendI(&endpoint->obqueue);
+            obqResetI(&endpoint->obqueue);
+            bqResumeX(&endpoint->obqueue);
+            osalOsRescheduleS();
+            osalSysUnlock();
+            continue;
+        }
+
+        if (!buffered) {
+            obqFlush(&endpoint->obqueue);
+        }
+
+        return true;
+    }
 }
 
-/** @} */
+void usb_endpoint_in_flush(usb_endpoint_in_t *endpoint, bool padded) {
+    osalDbgCheck(endpoint != NULL);
+
+    output_buffers_queue_t *obqp = &endpoint->obqueue;
+
+    if (padded && obqp->ptr != NULL) {
+        ptrdiff_t bytes_left = (size_t)obqp->top - (size_t)obqp->ptr;
+        while (bytes_left > 0) {
+            // Putting bytes into a buffer that has space left should never
+            // fail and be instant, therefore we don't check the return value
+            // for errors here.
+            obqPutTimeout(obqp, 0, TIME_IMMEDIATE);
+            bytes_left--;
+        }
+    }
+
+    obqFlush(obqp);
+}
+
+bool usb_endpoint_in_is_inactive(usb_endpoint_in_t *endpoint) {
+    osalDbgCheck(endpoint != NULL);
+
+    osalSysLock();
+    bool inactive = obqIsEmptyI(&endpoint->obqueue) && !usbGetTransmitStatusI(endpoint->config.usbp, endpoint->config.ep);
+    osalSysUnlock();
+
+    return inactive;
+}
+
+bool usb_endpoint_out_receive(usb_endpoint_out_t *endpoint, uint8_t *data, size_t size, sysinterval_t timeout) {
+    osalDbgCheck((endpoint != NULL) && (data != NULL) && (size > 0U));
+
+    osalSysLock();
+    if (usbGetDriverStateI(endpoint->config.usbp) != USB_ACTIVE) {
+        osalSysUnlock();
+        return false;
+    }
+
+    if (endpoint->timed_out && timeout != TIME_INFINITE) {
+        timeout = TIME_IMMEDIATE;
+    }
+    osalSysUnlock();
+
+    const size_t received = ibqReadTimeout(&endpoint->ibqueue, data, size, timeout);
+    endpoint->timed_out   = received == 0;
+
+    return received == size;
+}
diff --git a/tmk_core/protocol/chibios/usb_driver.h b/tmk_core/protocol/chibios/usb_driver.h
index f94387debd..26787e1eba 100644
--- a/tmk_core/protocol/chibios/usb_driver.h
+++ b/tmk_core/protocol/chibios/usb_driver.h
@@ -1,177 +1,209 @@
-/*
-    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
- * @{
- */
+// Copyright 2023 Stefan Kerkmann (@KarlK90)
+// Copyright 2020 Ryan (@fauxpark)
+// Copyright 2016 Fredizzimo
+// Copyright 2016 Giovanni Di Sirio
+// SPDX-License-Identifier: GPL-3.0-or-later OR Apache-2.0
 
 #pragma once
 
-#include <hal_usb_cdc.h>
-
-/*===========================================================================*/
-/* Driver constants.                                                         */
-/*===========================================================================*/
-
-/*===========================================================================*/
-/* Derived constants and error checks.                                       */
-/*===========================================================================*/
+#include <hal_buffers.h>
+#include "usb_descriptor.h"
+#include "chibios_config.h"
+#include "usb_report_handling.h"
+#include "string.h"
+#include "timer.h"
 
 #if HAL_USE_USB == FALSE
 #    error "The USB Driver requires HAL_USE_USB"
 #endif
 
-/*===========================================================================*/
-/* Driver data structures and types.                                         */
-/*===========================================================================*/
+/* USB Low Level driver specific endpoint fields */
+#if !defined(usb_lld_endpoint_fields)
+#    define usb_lld_endpoint_fields   \
+        2,        /* IN multiplier */ \
+            NULL, /* SETUP buffer (not a SETUP endpoint) */
+#endif
 
-/**
- * @brief Driver state machine possible states.
+/*
+ * Implementation notes:
+ *
+ * USBEndpointConfig - Configured using explicit order instead of struct member name.
+ *   This is due to ChibiOS hal LLD differences, which is dependent on hardware,
+ *   "USBv1" devices have `ep_buffers` and "OTGv1" have `in_multiplier`.
+ *   Given `USBv1/hal_usb_lld.h` marks the field as "not currently used" this code file
+ *   makes the assumption this is safe to avoid littering with preprocessor directives.
  */
-typedef enum {
-    QMKUSB_UNINIT = 0, /**< Not initialized.                   */
-    QMKUSB_STOP   = 1, /**< Stopped.                           */
-    QMKUSB_READY  = 2  /**< Ready.                             */
-} qmkusbstate_t;
+#define QMK_USB_ENDPOINT_IN(mode, ep_size, ep_num, _buffer_capacity, _usb_requests_cb, _report_storage) \
+    {                                                                                                   \
+        .usb_requests_cb = _usb_requests_cb, .report_storage = _report_storage,                         \
+        .ep_config =                                                                                    \
+            {                                                                                           \
+                mode,                           /* EP Mode */                                           \
+                NULL,                           /* SETUP packet notification callback */                \
+                usb_endpoint_in_tx_complete_cb, /* IN notification callback */                          \
+                NULL,                           /* OUT notification callback */                         \
+                ep_size,                        /* IN maximum packet size */                            \
+                0,                              /* OUT maximum packet size */                           \
+                NULL,                           /* IN Endpoint state */                                 \
+                NULL,                           /* OUT endpoint state */                                \
+                usb_lld_endpoint_fields         /* USB driver specific endpoint fields */               \
+            },                                                                                          \
+        .config = {                                                                                     \
+            .usbp            = &USB_DRIVER,                                                             \
+            .ep              = ep_num,                                                                  \
+            .buffer_capacity = _buffer_capacity,                                                        \
+            .buffer_size     = ep_size,                                                                 \
+            .buffer          = (_Alignas(4) uint8_t[BQ_BUFFER_SIZE(_buffer_capacity, ep_size)]){0},     \
+        }                                                                                               \
+    }
 
-/**
- * @brief   Structure representing a serial over USB driver.
- */
-typedef struct QMKUSBDriver QMKUSBDriver;
+#if !defined(USB_ENDPOINTS_ARE_REORDERABLE)
+
+#    define QMK_USB_ENDPOINT_OUT(mode, ep_size, ep_num, _buffer_capacity)                              \
+        {                                                                                              \
+            .ep_config =                                                                               \
+                {                                                                                      \
+                    mode,                            /* EP Mode */                                     \
+                    NULL,                            /* SETUP packet notification callback */          \
+                    NULL,                            /* IN notification callback */                    \
+                    usb_endpoint_out_rx_complete_cb, /* OUT notification callback */                   \
+                    0,                               /* IN maximum packet size */                      \
+                    ep_size,                         /* OUT maximum packet size */                     \
+                    NULL,                            /* IN Endpoint state */                           \
+                    NULL,                            /* OUT endpoint state */                          \
+                    usb_lld_endpoint_fields          /* USB driver specific endpoint fields */         \
+                },                                                                                     \
+            .config = {                                                                                \
+                .usbp            = &USB_DRIVER,                                                        \
+                .ep              = ep_num,                                                             \
+                .buffer_capacity = _buffer_capacity,                                                   \
+                .buffer_size     = ep_size,                                                            \
+                .buffer          = (_Alignas(4) uint8_t[BQ_BUFFER_SIZE(_buffer_capacity, ep_size)]){0} \
+            }                                                                                          \
+        }
+
+#else
+
+#    define QMK_USB_ENDPOINT_IN_SHARED(mode, ep_size, ep_num, _buffer_capacity, _usb_requests_cb, _report_storage) \
+        {                                                                                                          \
+            .usb_requests_cb = _usb_requests_cb, .is_shared = true, .report_storage = _report_storage,             \
+            .ep_config =                                                                                           \
+                {                                                                                                  \
+                    mode,                            /* EP Mode */                                                 \
+                    NULL,                            /* SETUP packet notification callback */                      \
+                    usb_endpoint_in_tx_complete_cb,  /* IN notification callback */                                \
+                    usb_endpoint_out_rx_complete_cb, /* OUT notification callback */                               \
+                    ep_size,                         /* IN maximum packet size */                                  \
+                    ep_size,                         /* OUT maximum packet size */                                 \
+                    NULL,                            /* IN Endpoint state */                                       \
+                    NULL,                            /* OUT endpoint state */                                      \
+                    usb_lld_endpoint_fields          /* USB driver specific endpoint fields */                     \
+                },                                                                                                 \
+            .config = {                                                                                            \
+                .usbp            = &USB_DRIVER,                                                                    \
+                .ep              = ep_num,                                                                         \
+                .buffer_capacity = _buffer_capacity,                                                               \
+                .buffer_size     = ep_size,                                                                        \
+                .buffer          = (_Alignas(4) uint8_t[BQ_BUFFER_SIZE(_buffer_capacity, ep_size)]){0},            \
+            }                                                                                                      \
+        }
+
+/* The current assumption is that there are no standalone OUT endpoints, so the
+ * OUT endpoint is always initialized by the IN endpoint. */
+#    define QMK_USB_ENDPOINT_OUT(mode, ep_size, ep_num, _buffer_capacity)                              \
+        {                                                                                              \
+            .ep_config =                                                                               \
+                {                                                                                      \
+                    0 /* Already defined in the IN endpoint */                                         \
+                },                                                                                     \
+            .config = {                                                                                \
+                .usbp            = &USB_DRIVER,                                                        \
+                .ep              = ep_num,                                                             \
+                .buffer_capacity = _buffer_capacity,                                                   \
+                .buffer_size     = ep_size,                                                            \
+                .buffer          = (_Alignas(4) uint8_t[BQ_BUFFER_SIZE(_buffer_capacity, ep_size)]){0} \
+            }                                                                                          \
+        }
+
+#endif
 
-/**
- * @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
+     * @brief   Endpoint used for data transfer
      */
-    size_t in_buffers;
-    size_t out_buffers;
+    usbep_t ep;
 
     /**
-     * @brief The size of each buffer in the queue, typically the same as the endpoint size
+     * @brief The number of buffers in the queue
      */
-    size_t in_size;
-    size_t out_size;
+    size_t buffer_capacity;
 
     /**
-     * @brief Always send full buffers in_size (the rest is filled with zeroes)
+     * @brief The size of each buffer in the queue, same as the endpoint size
      */
-    bool fixed_size;
+    size_t buffer_size;
 
-    /* Input buffer
-     * @note needs to be initialized with a memory buffer of the right size
+    /**
+     * @brief Buffer backing storage
      */
-    uint8_t *ib;
-    /* Output buffer
-     * @note needs to be initialized with a memory buffer of the right size
-     */
-    uint8_t *ob;
-} QMKUSBConfig;
+    uint8_t *buffer;
+} usb_endpoint_config_t;
 
-/**
- * @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;
+typedef struct {
+    output_buffers_queue_t obqueue;
+    USBEndpointConfig      ep_config;
+    USBInEndpointState     ep_in_state;
+#if defined(USB_ENDPOINTS_ARE_REORDERABLE)
+    USBOutEndpointState ep_out_state;
+    bool                is_shared;
+#endif
+    usb_endpoint_config_t config;
+    usbreqhandler_t       usb_requests_cb;
+    bool                  timed_out;
+    usb_report_storage_t *report_storage;
+} usb_endpoint_in_t;
 
-/**
- * @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.                                                    */
-/*===========================================================================*/
+typedef struct {
+    input_buffers_queue_t ibqueue;
+    USBEndpointConfig     ep_config;
+    USBOutEndpointState   ep_out_state;
+    usb_endpoint_config_t config;
+    bool                  timed_out;
+} usb_endpoint_out_t;
 
 #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);
+
+void usb_endpoint_in_init(usb_endpoint_in_t *endpoint);
+void usb_endpoint_in_start(usb_endpoint_in_t *endpoint);
+void usb_endpoint_in_stop(usb_endpoint_in_t *endpoint);
+
+bool usb_endpoint_in_send(usb_endpoint_in_t *endpoint, const uint8_t *data, size_t size, sysinterval_t timeout, bool buffered);
+void usb_endpoint_in_flush(usb_endpoint_in_t *endpoint, bool padded);
+bool usb_endpoint_in_is_inactive(usb_endpoint_in_t *endpoint);
+
+void usb_endpoint_in_suspend_cb(usb_endpoint_in_t *endpoint);
+void usb_endpoint_in_wakeup_cb(usb_endpoint_in_t *endpoint);
+void usb_endpoint_in_configure_cb(usb_endpoint_in_t *endpoint);
+void usb_endpoint_in_tx_complete_cb(USBDriver *usbp, usbep_t ep);
+
+void usb_endpoint_out_init(usb_endpoint_out_t *endpoint);
+void usb_endpoint_out_start(usb_endpoint_out_t *endpoint);
+void usb_endpoint_out_stop(usb_endpoint_out_t *endpoint);
+
+bool usb_endpoint_out_receive(usb_endpoint_out_t *endpoint, uint8_t *data, size_t size, sysinterval_t timeout);
+
+void usb_endpoint_out_suspend_cb(usb_endpoint_out_t *endpoint);
+void usb_endpoint_out_wakeup_cb(usb_endpoint_out_t *endpoint);
+void usb_endpoint_out_configure_cb(usb_endpoint_out_t *endpoint);
+void usb_endpoint_out_rx_complete_cb(USBDriver *usbp, usbep_t ep);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/tmk_core/protocol/chibios/usb_endpoints.c b/tmk_core/protocol/chibios/usb_endpoints.c
new file mode 100644
index 0000000000..37ec3c722f
--- /dev/null
+++ b/tmk_core/protocol/chibios/usb_endpoints.c
@@ -0,0 +1,152 @@
+// Copyright 2023 Stefan Kerkmann (@KarlK90)
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include <ch.h>
+#include <hal.h>
+
+#include "usb_main.h"
+#include "usb_driver.h"
+#include "usb_endpoints.h"
+#include "report.h"
+
+usb_endpoint_in_t usb_endpoints_in[USB_ENDPOINT_IN_COUNT] = {
+// clang-format off
+#if defined(SHARED_EP_ENABLE)
+    [USB_ENDPOINT_IN_SHARED] = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_INTR, SHARED_EPSIZE, SHARED_IN_EPNUM, SHARED_IN_CAPACITY, NULL,
+    QMK_USB_REPORT_STORAGE(
+        &usb_shared_get_report,
+        &usb_shared_set_report,
+        &usb_shared_reset_report,
+        &usb_shared_get_idle_rate,
+        &usb_shared_set_idle_rate,
+        &usb_shared_idle_timer_elapsed,
+        (REPORT_ID_COUNT + 1),
+#if defined(KEYBOARD_SHARED_EP)
+        QMK_USB_REPORT_STROAGE_ENTRY(REPORT_ID_KEYBOARD, sizeof(report_keyboard_t)),
+#endif
+#if defined(MOUSE_SHARED_EP)
+        QMK_USB_REPORT_STROAGE_ENTRY(REPORT_ID_MOUSE, sizeof(report_mouse_t)),
+#endif
+#if defined(EXTRAKEY_ENABLE)
+        QMK_USB_REPORT_STROAGE_ENTRY(REPORT_ID_SYSTEM, sizeof(report_extra_t)),
+        QMK_USB_REPORT_STROAGE_ENTRY(REPORT_ID_CONSUMER, sizeof(report_extra_t)),
+#endif
+#if defined(PROGRAMMABLE_BUTTON_ENABLE)
+        QMK_USB_REPORT_STROAGE_ENTRY(REPORT_ID_PROGRAMMABLE_BUTTON, sizeof(report_programmable_button_t)),
+#endif
+#if defined(NKRO_ENABLE)
+        QMK_USB_REPORT_STROAGE_ENTRY(REPORT_ID_NKRO, sizeof(report_nkro_t)),
+#endif
+#if defined(JOYSTICK_SHARED_EP)
+        QMK_USB_REPORT_STROAGE_ENTRY(REPORT_ID_JOYSTICK, sizeof(report_joystick_t)),
+#endif
+#if defined(DIGITIZER_SHARED_EP)
+        QMK_USB_REPORT_STROAGE_ENTRY(REPORT_ID_DIGITIZER, sizeof(report_digitizer_t)),
+#endif
+        )
+    ),
+#endif
+// clang-format on
+
+#if !defined(KEYBOARD_SHARED_EP)
+    [USB_ENDPOINT_IN_KEYBOARD] = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_INTR, KEYBOARD_EPSIZE, KEYBOARD_IN_EPNUM, KEYBOARD_IN_CAPACITY, NULL, QMK_USB_REPORT_STORAGE_DEFAULT(sizeof(report_keyboard_t))),
+#endif
+
+#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)
+    [USB_ENDPOINT_IN_MOUSE] = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_INTR, MOUSE_EPSIZE, MOUSE_IN_EPNUM, MOUSE_IN_CAPACITY, NULL, QMK_USB_REPORT_STORAGE_DEFAULT(sizeof(report_mouse_t))),
+#endif
+
+#if defined(JOYSTICK_ENABLE) && !defined(JOYSTICK_SHARED_EP)
+    [USB_ENDPOINT_IN_JOYSTICK] = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_INTR, JOYSTICK_EPSIZE, JOYSTICK_IN_EPNUM, JOYSTICK_IN_CAPACITY, QMK_USB_REPORT_STORAGE_DEFAULT(sizeof(report_joystick_t))),
+#endif
+
+#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP)
+    [USB_ENDPOINT_IN_JOYSTICK] = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_INTR, DIGITIZER_EPSIZE, DIGITIZER_IN_EPNUM, DIGITIZER_IN_CAPACITY, QMK_USB_REPORT_STORAGE_DEFAULT(sizeof(report_digitizer_t))),
+#endif
+
+#if defined(CONSOLE_ENABLE)
+#    if defined(USB_ENDPOINTS_ARE_REORDERABLE)
+    [USB_ENDPOINT_IN_CONSOLE] = QMK_USB_ENDPOINT_IN_SHARED(USB_EP_MODE_TYPE_INTR, CONSOLE_EPSIZE, CONSOLE_IN_EPNUM, CONSOLE_IN_CAPACITY, NULL, QMK_USB_REPORT_STORAGE_DEFAULT(CONSOLE_EPSIZE)),
+#    else
+    [USB_ENDPOINT_IN_CONSOLE]  = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_INTR, CONSOLE_EPSIZE, CONSOLE_IN_EPNUM, CONSOLE_IN_CAPACITY, NULL, QMK_USB_REPORT_STORAGE_DEFAULT(CONSOLE_EPSIZE)),
+#    endif
+#endif
+
+#if defined(RAW_ENABLE)
+#    if defined(USB_ENDPOINTS_ARE_REORDERABLE)
+    [USB_ENDPOINT_IN_RAW] = QMK_USB_ENDPOINT_IN_SHARED(USB_EP_MODE_TYPE_INTR, RAW_EPSIZE, RAW_IN_EPNUM, RAW_IN_CAPACITY, NULL, QMK_USB_REPORT_STORAGE_DEFAULT(RAW_EPSIZE)),
+#    else
+    [USB_ENDPOINT_IN_RAW]      = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_INTR, RAW_EPSIZE, RAW_IN_EPNUM, RAW_IN_CAPACITY, NULL, QMK_USB_REPORT_STORAGE_DEFAULT(RAW_EPSIZE)),
+#    endif
+#endif
+
+#if defined(MIDI_ENABLE)
+#    if defined(USB_ENDPOINTS_ARE_REORDERABLE)
+    [USB_ENDPOINT_IN_MIDI] = QMK_USB_ENDPOINT_IN_SHARED(USB_EP_MODE_TYPE_BULK, MIDI_STREAM_EPSIZE, MIDI_STREAM_IN_EPNUM, MIDI_STREAM_IN_CAPACITY, NULL, NULL),
+#    else
+    [USB_ENDPOINT_IN_MIDI]     = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_BULK, MIDI_STREAM_EPSIZE, MIDI_STREAM_IN_EPNUM, MIDI_STREAM_IN_CAPACITY, NULL, NULL),
+#    endif
+#endif
+
+#if defined(VIRTSER_ENABLE)
+#    if defined(USB_ENDPOINTS_ARE_REORDERABLE)
+    [USB_ENDPOINT_IN_CDC_DATA] = QMK_USB_ENDPOINT_IN_SHARED(USB_EP_MODE_TYPE_BULK, CDC_EPSIZE, CDC_IN_EPNUM, CDC_IN_CAPACITY, virtser_usb_request_cb, NULL),
+#    else
+    [USB_ENDPOINT_IN_CDC_DATA] = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_BULK, CDC_EPSIZE, CDC_IN_EPNUM, CDC_IN_CAPACITY, virtser_usb_request_cb, NULL),
+#    endif
+    [USB_ENDPOINT_IN_CDC_SIGNALING] = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_INTR, CDC_NOTIFICATION_EPSIZE, CDC_NOTIFICATION_EPNUM, CDC_SIGNALING_DUMMY_CAPACITY, NULL, NULL),
+#endif
+};
+
+usb_endpoint_in_lut_t usb_endpoint_interface_lut[TOTAL_INTERFACES] = {
+#if !defined(KEYBOARD_SHARED_EP)
+    [KEYBOARD_INTERFACE] = USB_ENDPOINT_IN_KEYBOARD,
+#endif
+
+#if defined(RAW_ENABLE)
+    [RAW_INTERFACE] = USB_ENDPOINT_IN_RAW,
+#endif
+
+#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)
+    [MOUSE_INTERFACE] = USB_ENDPOINT_IN_MOUSE,
+#endif
+
+#if defined(SHARED_EP_ENABLE)
+    [SHARED_INTERFACE] = USB_ENDPOINT_IN_SHARED,
+#endif
+
+#if defined(CONSOLE_ENABLE)
+    [CONSOLE_INTERFACE] = USB_ENDPOINT_IN_CONSOLE,
+#endif
+
+#if defined(MIDI_ENABLE)
+    [AS_INTERFACE] = USB_ENDPOINT_IN_MIDI,
+#endif
+
+#if defined(VIRTSER_ENABLE)
+    [CCI_INTERFACE] = USB_ENDPOINT_IN_CDC_SIGNALING,
+    [CDI_INTERFACE] = USB_ENDPOINT_IN_CDC_DATA,
+#endif
+
+#if defined(JOYSTICK_ENABLE) && !defined(JOYSTICK_SHARED_EP)
+    [JOYSTICK_INTERFACE] = USB_ENDPOINT_IN_JOYSTICK,
+#endif
+
+#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP)
+    [DIGITIZER_INTERFACE] = USB_ENDPOINT_IN_DIGITIZER,
+#endif
+};
+
+usb_endpoint_out_t usb_endpoints_out[USB_ENDPOINT_OUT_COUNT] = {
+#if defined(RAW_ENABLE)
+    [USB_ENDPOINT_OUT_RAW] = QMK_USB_ENDPOINT_OUT(USB_EP_MODE_TYPE_INTR, RAW_EPSIZE, RAW_OUT_EPNUM, RAW_OUT_CAPACITY),
+#endif
+
+#if defined(MIDI_ENABLE)
+    [USB_ENDPOINT_OUT_MIDI] = QMK_USB_ENDPOINT_OUT(USB_EP_MODE_TYPE_BULK, MIDI_STREAM_EPSIZE, MIDI_STREAM_OUT_EPNUM, MIDI_STREAM_OUT_CAPACITY),
+#endif
+
+#if defined(VIRTSER_ENABLE)
+    [USB_ENDPOINT_OUT_CDC_DATA] = QMK_USB_ENDPOINT_OUT(USB_EP_MODE_TYPE_BULK, CDC_EPSIZE, CDC_OUT_EPNUM, CDC_OUT_CAPACITY),
+#endif
+};
diff --git a/tmk_core/protocol/chibios/usb_endpoints.h b/tmk_core/protocol/chibios/usb_endpoints.h
new file mode 100644
index 0000000000..a4e5ed88fc
--- /dev/null
+++ b/tmk_core/protocol/chibios/usb_endpoints.h
@@ -0,0 +1,137 @@
+// Copyright 2023 Stefan Kerkmann (@KarlK90)
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "usb_descriptor.h"
+
+#if !defined(USB_DEFAULT_BUFFER_CAPACITY)
+#    define USB_DEFAULT_BUFFER_CAPACITY 4
+#endif
+
+#if !defined(KEYBOARD_IN_CAPACITY)
+#    define KEYBOARD_IN_CAPACITY USB_DEFAULT_BUFFER_CAPACITY
+#endif
+#if !defined(SHARED_IN_CAPACITY)
+#    define SHARED_IN_CAPACITY USB_DEFAULT_BUFFER_CAPACITY
+#endif
+#if !defined(MOUSE_IN_CAPACITY)
+#    define MOUSE_IN_CAPACITY USB_DEFAULT_BUFFER_CAPACITY
+#endif
+
+#if !defined(JOYSTICK_IN_CAPACITY)
+#    define JOYSTICK_IN_CAPACITY USB_DEFAULT_BUFFER_CAPACITY
+#endif
+
+#if !defined(DIGITIZER_IN_CAPACITY)
+#    define DIGITIZER_IN_CAPACITY USB_DEFAULT_BUFFER_CAPACITY
+#endif
+
+#if !defined(CONSOLE_IN_CAPACITY)
+#    define CONSOLE_IN_CAPACITY USB_DEFAULT_BUFFER_CAPACITY
+#endif
+
+#if !defined(CONSOLE_OUT_CAPACITY)
+#    define CONSOLE_OUT_CAPACITY USB_DEFAULT_BUFFER_CAPACITY
+#endif
+
+#if !defined(RAW_IN_CAPACITY)
+#    define RAW_IN_CAPACITY USB_DEFAULT_BUFFER_CAPACITY
+#endif
+
+#if !defined(RAW_OUT_CAPACITY)
+#    define RAW_OUT_CAPACITY USB_DEFAULT_BUFFER_CAPACITY
+#endif
+
+#if !defined(MIDI_STREAM_IN_CAPACITY)
+#    define MIDI_STREAM_IN_CAPACITY USB_DEFAULT_BUFFER_CAPACITY
+#endif
+
+#if !defined(MIDI_STREAM_OUT_CAPACITY)
+#    define MIDI_STREAM_OUT_CAPACITY USB_DEFAULT_BUFFER_CAPACITY
+#endif
+
+#if !defined(CDC_IN_CAPACITY)
+#    define CDC_IN_CAPACITY USB_DEFAULT_BUFFER_CAPACITY
+#endif
+
+#if !defined(CDC_OUT_CAPACITY)
+#    define CDC_OUT_CAPACITY USB_DEFAULT_BUFFER_CAPACITY
+#endif
+
+#define CDC_SIGNALING_DUMMY_CAPACITY 1
+
+typedef enum {
+#if defined(SHARED_EP_ENABLE)
+    USB_ENDPOINT_IN_SHARED,
+#endif
+
+#if !defined(KEYBOARD_SHARED_EP)
+    USB_ENDPOINT_IN_KEYBOARD,
+#endif
+
+#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)
+    USB_ENDPOINT_IN_MOUSE,
+#endif
+
+#if defined(JOYSTICK_ENABLE) && !defined(JOYSTICK_SHARED_EP)
+    USB_ENDPOINT_IN_JOYSTICK,
+#endif
+
+#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP)
+    USB_ENDPOINT_IN_DIGITIZER,
+#endif
+
+#if defined(CONSOLE_ENABLE)
+    USB_ENDPOINT_IN_CONSOLE,
+#endif
+
+#if defined(RAW_ENABLE)
+    USB_ENDPOINT_IN_RAW,
+#endif
+
+#if defined(MIDI_ENABLE)
+    USB_ENDPOINT_IN_MIDI,
+#endif
+
+#if defined(VIRTSER_ENABLE)
+    USB_ENDPOINT_IN_CDC_DATA,
+    USB_ENDPOINT_IN_CDC_SIGNALING,
+#endif
+    USB_ENDPOINT_IN_COUNT,
+/* All non shared endpoints have to be consequtive numbers starting from 0, so
+ * that they can be used as array indices. The shared endpoints all point to
+ * the same endpoint so they have to be defined last to not reset the enum
+ * counter. */
+#if defined(SHARED_EP_ENABLE)
+#    if defined(KEYBOARD_SHARED_EP)
+    USB_ENDPOINT_IN_KEYBOARD = USB_ENDPOINT_IN_SHARED,
+#    endif
+#    if defined(MOUSE_SHARED_EP)
+    USB_ENDPOINT_IN_MOUSE = USB_ENDPOINT_IN_SHARED,
+#    endif
+#    if defined(JOYSTICK_SHARED_EP)
+    USB_ENDPOINT_IN_JOYSTICK = USB_ENDPOINT_IN_SHARED,
+#    endif
+#    if defined(DIGITIZER_SHARED_EP)
+    USB_ENDPOINT_IN_DIGITIZER = USB_ENDPOINT_IN_SHARED,
+#    endif
+#endif
+} usb_endpoint_in_lut_t;
+
+#define IS_VALID_USB_ENDPOINT_IN_LUT(i) ((i) >= 0 && (i) < USB_ENDPOINT_IN_COUNT)
+
+usb_endpoint_in_lut_t usb_endpoint_interface_lut[TOTAL_INTERFACES];
+
+typedef enum {
+#if defined(RAW_ENABLE)
+    USB_ENDPOINT_OUT_RAW,
+#endif
+#if defined(MIDI_ENABLE)
+    USB_ENDPOINT_OUT_MIDI,
+#endif
+#if defined(VIRTSER_ENABLE)
+    USB_ENDPOINT_OUT_CDC_DATA,
+#endif
+    USB_ENDPOINT_OUT_COUNT,
+} usb_endpoint_out_lut_t;
diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c
index 7b1e641213..ced5fd4fc2 100644
--- a/tmk_core/protocol/chibios/usb_main.c
+++ b/tmk_core/protocol/chibios/usb_main.c
@@ -1,45 +1,32 @@
-/*
- * (c) 2015 flabberast <s3+flabbergast@sdfeu.org>
- *
- * Based on the following work:
- *  - Guillaume Duc's raw hid example (MIT License)
- *    https://github.com/guiduc/usb-hid-chibios-example
- *  - PJRC Teensy examples (MIT License)
- *    https://www.pjrc.com/teensy/usb_keyboard.html
- *  - hasu's TMK keyboard code (GPL v2 and some code Modified BSD)
- *    https://github.com/tmk/tmk_keyboard/
- *  - ChibiOS demo code (Apache 2.0 License)
- *    http://www.chibios.org
- *
- * Since some GPL'd code is used, this work is licensed under
- * GPL v2 or later.
- */
-
-/*
- * Implementation notes:
- *
- * USBEndpointConfig - Configured using explicit order instead of struct member name.
- *   This is due to ChibiOS hal LLD differences, which is dependent on hardware,
- *   "USBv1" devices have `ep_buffers` and "OTGv1" have `in_multiplier`.
- *   Given `USBv1/hal_usb_lld.h` marks the field as "not currently used" this code file
- *   makes the assumption this is safe to avoid littering with preprocessor directives.
- */
+// Copyright 2023 Stefan Kerkmann
+// Copyright 2020-2021 Ryan (@fauxpark)
+// Copyright 2020 Nick Brassel (@tzarc)
+// Copyright 2020 a-chol
+// Copyright 2020 xyzz
+// Copyright 2020 Joel Challis (@zvecr)
+// Copyright 2020 George (@goshdarnharris)
+// Copyright 2018 James Laird-Wah
+// Copyright 2018 Drashna Jaelre (@drashna)
+// Copyright 2016 Fredizzimo
+// Copyright 2016 Giovanni Di Sirio
+// SPDX-License-Identifier: GPL-3.0-or-later OR Apache-2.0
 
 #include <ch.h>
 #include <hal.h>
 #include <string.h>
 
 #include "usb_main.h"
+#include "usb_report_handling.h"
 
 #include "host.h"
-#include "chibios_config.h"
-#include "debug.h"
 #include "suspend.h"
+#include "timer.h"
 #ifdef SLEEP_LED_ENABLE
 #    include "sleep_led.h"
 #    include "led.h"
 #endif
 #include "wait.h"
+#include "usb_endpoints.h"
 #include "usb_device_state.h"
 #include "usb_descriptor.h"
 #include "usb_driver.h"
@@ -51,11 +38,6 @@
 extern keymap_config_t keymap_config;
 #endif
 
-#if defined(CONSOLE_ENABLE)
-#    define RBUF_SIZE 256
-#    include "ring_buffer.h"
-#endif
-
 /* ---------------------------------------------------------
  *       Global interface variables and declarations
  * ---------------------------------------------------------
@@ -69,33 +51,16 @@ extern keymap_config_t keymap_config;
 #    define usb_lld_disconnect_bus(usbp)
 #endif
 
-uint8_t                keyboard_idle __attribute__((aligned(2)))     = 0;
-uint8_t                keyboard_protocol __attribute__((aligned(2))) = 1;
-uint8_t                keyboard_led_state                            = 0;
-volatile uint16_t      keyboard_idle_count                           = 0;
-static virtual_timer_t keyboard_idle_timer;
+extern usb_endpoint_in_t  usb_endpoints_in[USB_ENDPOINT_IN_COUNT];
+extern usb_endpoint_out_t usb_endpoints_out[USB_ENDPOINT_OUT_COUNT];
 
-static void keyboard_idle_timer_cb(struct ch_virtual_timer *, void *arg);
+uint8_t _Alignas(2) keyboard_idle     = 0;
+uint8_t _Alignas(2) keyboard_protocol = 1;
+uint8_t keyboard_led_state            = 0;
 
-report_keyboard_t keyboard_report_sent = {0};
-report_mouse_t    mouse_report_sent    = {0};
-
-union {
-    uint8_t           report_id;
-    report_keyboard_t keyboard;
-#ifdef EXTRAKEY_ENABLE
-    report_extra_t extra;
-#endif
-#ifdef MOUSE_ENABLE
-    report_mouse_t mouse;
-#endif
-#ifdef DIGITIZER_ENABLE
-    report_digitizer_t digitizer;
-#endif
-#ifdef JOYSTICK_ENABLE
-    report_joystick_t joystick;
-#endif
-} universal_report_blank = {0};
+static bool __attribute__((__unused__)) send_report_buffered(usb_endpoint_in_lut_t endpoint, void *report, size_t size);
+static void __attribute__((__unused__)) flush_report_buffered(usb_endpoint_in_lut_t endpoint, bool padded);
+static bool __attribute__((__unused__)) receive_report(usb_endpoint_out_lut_t endpoint, void *report, size_t size);
 
 /* ---------------------------------------------------------
  *            Descriptors and USB driver objects
@@ -109,6 +74,11 @@ union {
             NULL, /* SETUP buffer (not a SETUP endpoint) */
 #endif
 
+/*
+ * Handles the GET_DESCRIPTOR callback
+ *
+ * Returns the proper descriptor
+ */
 static const USBDescriptor *usb_get_descriptor_cb(USBDriver *usbp, uint8_t dtype, uint8_t dindex, uint16_t wIndex) {
     usb_control_request_t *setup = (usb_control_request_t *)usbp->setup;
 
@@ -123,299 +93,6 @@ static const USBDescriptor *usb_get_descriptor_cb(USBDriver *usbp, uint8_t dtype
     return &descriptor;
 }
 
-/*
- * USB notification callback that does nothing.  Needed to work around bugs in
- * some USB LLDs that fail to resume the waiting thread when the notification
- * callback pointer is NULL.
- */
-static void dummy_usb_cb(USBDriver *usbp, usbep_t ep) {
-    (void)usbp;
-    (void)ep;
-}
-
-#ifndef KEYBOARD_SHARED_EP
-/* keyboard endpoint state structure */
-static USBInEndpointState kbd_ep_state;
-/* keyboard endpoint initialization structure (IN) - see USBEndpointConfig comment at top of file */
-static const USBEndpointConfig kbd_ep_config = {
-    USB_EP_MODE_TYPE_INTR,  /* Interrupt EP */
-    NULL,                   /* SETUP packet notification callback */
-    dummy_usb_cb,           /* IN notification callback */
-    NULL,                   /* OUT notification callback */
-    KEYBOARD_EPSIZE,        /* IN maximum packet size */
-    0,                      /* OUT maximum packet size */
-    &kbd_ep_state,          /* IN Endpoint state */
-    NULL,                   /* OUT endpoint state */
-    usb_lld_endpoint_fields /* USB driver specific endpoint fields */
-};
-#endif
-
-#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)
-/* mouse endpoint state structure */
-static USBInEndpointState mouse_ep_state;
-
-/* mouse endpoint initialization structure (IN) - see USBEndpointConfig comment at top of file */
-static const USBEndpointConfig mouse_ep_config = {
-    USB_EP_MODE_TYPE_INTR,  /* Interrupt EP */
-    NULL,                   /* SETUP packet notification callback */
-    dummy_usb_cb,           /* IN notification callback */
-    NULL,                   /* OUT notification callback */
-    MOUSE_EPSIZE,           /* IN maximum packet size */
-    0,                      /* OUT maximum packet size */
-    &mouse_ep_state,        /* IN Endpoint state */
-    NULL,                   /* OUT endpoint state */
-    usb_lld_endpoint_fields /* USB driver specific endpoint fields */
-};
-#endif
-
-#ifdef SHARED_EP_ENABLE
-/* shared endpoint state structure */
-static USBInEndpointState shared_ep_state;
-
-/* shared endpoint initialization structure (IN) - see USBEndpointConfig comment at top of file */
-static const USBEndpointConfig shared_ep_config = {
-    USB_EP_MODE_TYPE_INTR,  /* Interrupt EP */
-    NULL,                   /* SETUP packet notification callback */
-    dummy_usb_cb,           /* IN notification callback */
-    NULL,                   /* OUT notification callback */
-    SHARED_EPSIZE,          /* IN maximum packet size */
-    0,                      /* OUT maximum packet size */
-    &shared_ep_state,       /* IN Endpoint state */
-    NULL,                   /* OUT endpoint state */
-    usb_lld_endpoint_fields /* USB driver specific endpoint fields */
-};
-#endif
-
-#if defined(JOYSTICK_ENABLE) && !defined(JOYSTICK_SHARED_EP)
-/* joystick endpoint state structure */
-static USBInEndpointState joystick_ep_state;
-
-/* joystick endpoint initialization structure (IN) - see USBEndpointConfig comment at top of file */
-static const USBEndpointConfig joystick_ep_config = {
-    USB_EP_MODE_TYPE_INTR,  /* Interrupt EP */
-    NULL,                   /* SETUP packet notification callback */
-    dummy_usb_cb,           /* IN notification callback */
-    NULL,                   /* OUT notification callback */
-    JOYSTICK_EPSIZE,        /* IN maximum packet size */
-    0,                      /* OUT maximum packet size */
-    &joystick_ep_state,     /* IN Endpoint state */
-    NULL,                   /* OUT endpoint state */
-    usb_lld_endpoint_fields /* USB driver specific endpoint fields */
-};
-#endif
-
-#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP)
-/* digitizer endpoint state structure */
-static USBInEndpointState digitizer_ep_state;
-
-/* digitizer endpoint initialization structure (IN) - see USBEndpointConfig comment at top of file */
-static const USBEndpointConfig digitizer_ep_config = {
-    USB_EP_MODE_TYPE_INTR,  /* Interrupt EP */
-    NULL,                   /* SETUP packet notification callback */
-    dummy_usb_cb,           /* IN notification callback */
-    NULL,                   /* OUT notification callback */
-    DIGITIZER_EPSIZE,       /* IN maximum packet size */
-    0,                      /* OUT maximum packet size */
-    &digitizer_ep_state,    /* IN Endpoint state */
-    NULL,                   /* OUT endpoint state */
-    usb_lld_endpoint_fields /* USB driver specific endpoint fields */
-};
-#endif
-
-#ifdef CONSOLE_ENABLE
-/* Console endpoint state structure */
-static USBInEndpointState console_ep_state;
-
-/* Console endpoint initialization structure (IN) - see USBEndpointConfig comment at top of file */
-static const USBEndpointConfig console_ep_config = {
-    USB_EP_MODE_TYPE_INTR,  /* Interrupt EP */
-    NULL,                   /* SETUP packet notification callback */
-    dummy_usb_cb,           /* IN notification callback */
-    NULL,                   /* OUT notification callback */
-    CONSOLE_EPSIZE,         /* IN maximum packet size */
-    0,                      /* OUT maximum packet size */
-    &console_ep_state,      /* IN Endpoint state */
-    NULL,                   /* OUT endpoint state */
-    usb_lld_endpoint_fields /* USB driver specific endpoint fields */
-};
-#endif
-
-#ifdef USB_ENDPOINTS_ARE_REORDERABLE
-typedef struct {
-    size_t              queue_capacity_in;
-    size_t              queue_capacity_out;
-    USBInEndpointState  in_ep_state;
-    USBOutEndpointState out_ep_state;
-    USBInEndpointState  int_ep_state;
-    USBEndpointConfig   inout_ep_config;
-    USBEndpointConfig   int_ep_config;
-    const QMKUSBConfig  config;
-    QMKUSBDriver        driver;
-} usb_driver_config_t;
-#else
-typedef struct {
-    size_t              queue_capacity_in;
-    size_t              queue_capacity_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 QMKUSBConfig  config;
-    QMKUSBDriver        driver;
-} usb_driver_config_t;
-#endif
-
-#ifdef USB_ENDPOINTS_ARE_REORDERABLE
-/* Reusable initialization structure - see USBEndpointConfig comment at top of file */
-#    define QMK_USB_DRIVER_CONFIG(stream, notification, fixedsize)                                                              \
-        {                                                                                                                       \
-            .queue_capacity_in = stream##_IN_CAPACITY, .queue_capacity_out = stream##_OUT_CAPACITY,                             \
-            .inout_ep_config =                                                                                                  \
-                {                                                                                                               \
-                    stream##_IN_MODE,       /* Interrupt EP */                                                                  \
-                    NULL,                   /* SETUP packet notification callback */                                            \
-                    qmkusbDataTransmitted,  /* IN notification callback */                                                      \
-                    qmkusbDataReceived,     /* OUT notification callback */                                                     \
-                    stream##_EPSIZE,        /* IN maximum packet size */                                                        \
-                    stream##_EPSIZE,        /* OUT maximum packet size */                                                       \
-                    NULL,                   /* IN Endpoint state */                                                             \
-                    NULL,                   /* OUT endpoint state */                                                            \
-                    usb_lld_endpoint_fields /* USB driver specific endpoint fields */                                           \
-                },                                                                                                              \
-            .int_ep_config =                                                                                                    \
-                {                                                                                                               \
-                    USB_EP_MODE_TYPE_INTR,      /* Interrupt EP */                                                              \
-                    NULL,                       /* SETUP packet notification callback */                                        \
-                    qmkusbInterruptTransmitted, /* IN notification callback */                                                  \
-                    NULL,                       /* OUT notification callback */                                                 \
-                    CDC_NOTIFICATION_EPSIZE,    /* IN maximum packet size */                                                    \
-                    0,                          /* OUT maximum packet size */                                                   \
-                    NULL,                       /* IN Endpoint state */                                                         \
-                    NULL,                       /* OUT endpoint state */                                                        \
-                    usb_lld_endpoint_fields     /* USB driver specific endpoint fields */                                       \
-                },                                                                                                              \
-            .config = {                                                                                                         \
-                .usbp        = &USB_DRIVER,                                                                                     \
-                .bulk_in     = stream##_IN_EPNUM,                                                                               \
-                .bulk_out    = stream##_OUT_EPNUM,                                                                              \
-                .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          = (__attribute__((aligned(4))) uint8_t[BQ_BUFFER_SIZE(stream##_IN_CAPACITY, stream##_EPSIZE)]){},  \
-                .ob          = (__attribute__((aligned(4))) uint8_t[BQ_BUFFER_SIZE(stream##_OUT_CAPACITY, stream##_EPSIZE)]){}, \
-            }                                                                                                                   \
-        }
-#else
-/* Reusable initialization structure - see USBEndpointConfig comment at top of file */
-#    define QMK_USB_DRIVER_CONFIG(stream, notification, fixedsize)                                                              \
-        {                                                                                                                       \
-            .queue_capacity_in = stream##_IN_CAPACITY, .queue_capacity_out = stream##_OUT_CAPACITY,                             \
-            .in_ep_config =                                                                                                     \
-                {                                                                                                               \
-                    stream##_IN_MODE,       /* Interrupt EP */                                                                  \
-                    NULL,                   /* SETUP packet notification callback */                                            \
-                    qmkusbDataTransmitted,  /* IN notification callback */                                                      \
-                    NULL,                   /* OUT notification callback */                                                     \
-                    stream##_EPSIZE,        /* IN maximum packet size */                                                        \
-                    0,                      /* OUT maximum packet size */                                                       \
-                    NULL,                   /* IN Endpoint state */                                                             \
-                    NULL,                   /* OUT endpoint state */                                                            \
-                    usb_lld_endpoint_fields /* USB driver specific endpoint fields */                                           \
-                },                                                                                                              \
-            .out_ep_config =                                                                                                    \
-                {                                                                                                               \
-                    stream##_OUT_MODE,      /* Interrupt EP */                                                                  \
-                    NULL,                   /* SETUP packet notification callback */                                            \
-                    NULL,                   /* IN notification callback */                                                      \
-                    qmkusbDataReceived,     /* OUT notification callback */                                                     \
-                    0,                      /* IN maximum packet size */                                                        \
-                    stream##_EPSIZE,        /* OUT maximum packet size */                                                       \
-                    NULL,                   /* IN Endpoint state */                                                             \
-                    NULL,                   /* OUT endpoint state */                                                            \
-                    usb_lld_endpoint_fields /* USB driver specific endpoint fields */                                           \
-                },                                                                                                              \
-            .int_ep_config =                                                                                                    \
-                {                                                                                                               \
-                    USB_EP_MODE_TYPE_INTR,      /* Interrupt EP */                                                              \
-                    NULL,                       /* SETUP packet notification callback */                                        \
-                    qmkusbInterruptTransmitted, /* IN notification callback */                                                  \
-                    NULL,                       /* OUT notification callback */                                                 \
-                    CDC_NOTIFICATION_EPSIZE,    /* IN maximum packet size */                                                    \
-                    0,                          /* OUT maximum packet size */                                                   \
-                    NULL,                       /* IN Endpoint state */                                                         \
-                    NULL,                       /* OUT endpoint state */                                                        \
-                    usb_lld_endpoint_fields     /* USB driver specific endpoint fields */                                       \
-                },                                                                                                              \
-            .config = {                                                                                                         \
-                .usbp        = &USB_DRIVER,                                                                                     \
-                .bulk_in     = stream##_IN_EPNUM,                                                                               \
-                .bulk_out    = stream##_OUT_EPNUM,                                                                              \
-                .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          = (__attribute__((aligned(4))) uint8_t[BQ_BUFFER_SIZE(stream##_IN_CAPACITY, stream##_EPSIZE)]){},  \
-                .ob          = (__attribute__((aligned(4))) uint8_t[BQ_BUFFER_SIZE(stream##_OUT_CAPACITY, stream##_EPSIZE)]){}, \
-            }                                                                                                                   \
-        }
-#endif
-
-typedef struct {
-    union {
-        struct {
-#ifdef RAW_ENABLE
-            usb_driver_config_t raw_driver;
-#endif
-#ifdef MIDI_ENABLE
-            usb_driver_config_t midi_driver;
-#endif
-#ifdef VIRTSER_ENABLE
-            usb_driver_config_t serial_driver;
-#endif
-        };
-        usb_driver_config_t array[0];
-    };
-} usb_driver_configs_t;
-
-static usb_driver_configs_t drivers = {
-#ifdef RAW_ENABLE
-#    ifndef RAW_IN_CAPACITY
-#        define RAW_IN_CAPACITY 4
-#    endif
-#    ifndef RAW_OUT_CAPACITY
-#        define RAW_OUT_CAPACITY 4
-#    endif
-#    define RAW_IN_MODE USB_EP_MODE_TYPE_INTR
-#    define RAW_OUT_MODE USB_EP_MODE_TYPE_INTR
-    .raw_driver = QMK_USB_DRIVER_CONFIG(RAW, 0, false),
-#endif
-
-#ifdef MIDI_ENABLE
-#    define MIDI_STREAM_IN_CAPACITY 4
-#    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 = QMK_USB_DRIVER_CONFIG(MIDI_STREAM, 0, false),
-#endif
-
-#ifdef VIRTSER_ENABLE
-#    define CDC_IN_CAPACITY 4
-#    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 = QMK_USB_DRIVER_CONFIG(CDC, CDC_NOTIFICATION_EPNUM, false),
-#endif
-};
-
-#define NUM_USB_DRIVERS (sizeof(drivers) / sizeof(usb_driver_config_t))
-
 /* ---------------------------------------------------------
  *                  USB driver functions
  * ---------------------------------------------------------
@@ -507,36 +184,11 @@ static void usb_event_cb(USBDriver *usbp, usbevent_t event) {
 
         case USB_EVENT_CONFIGURED:
             osalSysLockFromISR();
-            /* Enable the endpoints specified into the configuration. */
-#ifndef KEYBOARD_SHARED_EP
-            usbInitEndpointI(usbp, KEYBOARD_IN_EPNUM, &kbd_ep_config);
-#endif
-#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)
-            usbInitEndpointI(usbp, MOUSE_IN_EPNUM, &mouse_ep_config);
-#endif
-#ifdef SHARED_EP_ENABLE
-            usbInitEndpointI(usbp, SHARED_IN_EPNUM, &shared_ep_config);
-#endif
-#if defined(JOYSTICK_ENABLE) && !defined(JOYSTICK_SHARED_EP)
-            usbInitEndpointI(usbp, JOYSTICK_IN_EPNUM, &joystick_ep_config);
-#endif
-#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP)
-            usbInitEndpointI(usbp, DIGITIZER_IN_EPNUM, &digitizer_ep_config);
-#endif
-#ifdef CONSOLE_ENABLE
-            usbInitEndpointI(usbp, CONSOLE_IN_EPNUM, &console_ep_config);
-#endif
-            for (int i = 0; i < NUM_USB_DRIVERS; i++) {
-#ifdef USB_ENDPOINTS_ARE_REORDERABLE
-                usbInitEndpointI(usbp, drivers.array[i].config.bulk_in, &drivers.array[i].inout_ep_config);
-#else
-                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);
-#endif
-                if (drivers.array[i].config.int_in) {
-                    usbInitEndpointI(usbp, drivers.array[i].config.int_in, &drivers.array[i].int_ep_config);
-                }
-                qmkusbConfigureHookI(&drivers.array[i].driver);
+            for (int i = 0; i < USB_ENDPOINT_IN_COUNT; i++) {
+                usb_endpoint_in_configure_cb(&usb_endpoints_in[i]);
+            }
+            for (int i = 0; i < USB_ENDPOINT_OUT_COUNT; i++) {
+                usb_endpoint_out_configure_cb(&usb_endpoints_out[i]);
             }
             osalSysUnlockFromISR();
             if (last_suspend_state) {
@@ -550,22 +202,25 @@ static void usb_event_cb(USBDriver *usbp, usbevent_t event) {
             /* Falls into.*/
         case USB_EVENT_RESET:
             usb_event_queue_enqueue(event);
-            for (int i = 0; i < NUM_USB_DRIVERS; i++) {
-                chSysLockFromISR();
-                /* Disconnection event on suspend.*/
-                qmkusbSuspendHookI(&drivers.array[i].driver);
-                chSysUnlockFromISR();
+            chSysLockFromISR();
+            for (int i = 0; i < USB_ENDPOINT_IN_COUNT; i++) {
+                usb_endpoint_in_suspend_cb(&usb_endpoints_in[i]);
             }
+            for (int i = 0; i < USB_ENDPOINT_OUT_COUNT; i++) {
+                usb_endpoint_out_suspend_cb(&usb_endpoints_out[i]);
+            }
+            chSysUnlockFromISR();
             return;
 
         case USB_EVENT_WAKEUP:
-            // TODO: from ISR! print("[W]");
-            for (int i = 0; i < NUM_USB_DRIVERS; i++) {
-                chSysLockFromISR();
-                /* Disconnection event on suspend.*/
-                qmkusbWakeupHookI(&drivers.array[i].driver);
-                chSysUnlockFromISR();
+            chSysLockFromISR();
+            for (int i = 0; i < USB_ENDPOINT_IN_COUNT; i++) {
+                usb_endpoint_in_wakeup_cb(&usb_endpoints_in[i]);
             }
+            for (int i = 0; i < USB_ENDPOINT_OUT_COUNT; i++) {
+                usb_endpoint_out_wakeup_cb(&usb_endpoints_out[i]);
+            }
+            chSysUnlockFromISR();
             usb_event_queue_enqueue(USB_EVENT_WAKEUP);
             return;
 
@@ -587,7 +242,7 @@ static void usb_event_cb(USBDriver *usbp, usbevent_t event) {
  * Other Device    Required    Optional    Optional    Optional    Optional    Optional
  */
 
-static uint8_t set_report_buf[2] __attribute__((aligned(4)));
+static uint8_t _Alignas(4) set_report_buf[2];
 
 static void set_led_transfer_cb(USBDriver *usbp) {
     usb_control_request_t *setup = (usb_control_request_t *)usbp->setup;
@@ -611,41 +266,7 @@ static bool usb_requests_hook_cb(USBDriver *usbp) {
             case USB_RTYPE_DIR_DEV2HOST:
                 switch (setup->bRequest) {
                     case HID_REQ_GetReport:
-                        switch (setup->wIndex) {
-#ifndef KEYBOARD_SHARED_EP
-                            case KEYBOARD_INTERFACE:
-                                usbSetupTransfer(usbp, (uint8_t *)&keyboard_report_sent, KEYBOARD_REPORT_SIZE, NULL);
-                                return TRUE;
-                                break;
-#endif
-#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)
-                            case MOUSE_INTERFACE:
-                                usbSetupTransfer(usbp, (uint8_t *)&mouse_report_sent, sizeof(mouse_report_sent), NULL);
-                                return TRUE;
-                                break;
-#endif
-#ifdef SHARED_EP_ENABLE
-                            case SHARED_INTERFACE:
-#    ifdef KEYBOARD_SHARED_EP
-                                if (setup->wValue.lbyte == REPORT_ID_KEYBOARD) {
-                                    usbSetupTransfer(usbp, (uint8_t *)&keyboard_report_sent, KEYBOARD_REPORT_SIZE, NULL);
-                                    return true;
-                                }
-#    endif
-#    ifdef MOUSE_SHARED_EP
-                                if (setup->wValue.lbyte == REPORT_ID_MOUSE) {
-                                    usbSetupTransfer(usbp, (uint8_t *)&mouse_report_sent, sizeof(mouse_report_sent), NULL);
-                                    return true;
-                                }
-#    endif
-#endif /* SHARED_EP_ENABLE */
-                            default:
-                                universal_report_blank.report_id = setup->wValue.lbyte;
-                                usbSetupTransfer(usbp, (uint8_t *)&universal_report_blank, setup->wLength, NULL);
-                                return true;
-                        }
-                        break;
-
+                        return usb_get_report_cb(usbp);
                     case HID_REQ_GetProtocol:
                         if (setup->wIndex == KEYBOARD_INTERFACE) {
                             usbSetupTransfer(usbp, &keyboard_protocol, sizeof(uint8_t), NULL);
@@ -654,10 +275,8 @@ static bool usb_requests_hook_cb(USBDriver *usbp) {
                         break;
 
                     case HID_REQ_GetIdle:
-                        usbSetupTransfer(usbp, &keyboard_idle, sizeof(uint8_t), NULL);
-                        return true;
+                        return usb_get_idle_cb(usbp);
                 }
-                break;
 
             case USB_RTYPE_DIR_HOST2DEV:
                 switch (setup->bRequest) {
@@ -671,38 +290,15 @@ static bool usb_requests_hook_cb(USBDriver *usbp) {
                                 return true;
                         }
                         break;
-
                     case HID_REQ_SetProtocol:
                         if (setup->wIndex == KEYBOARD_INTERFACE) {
                             keyboard_protocol = setup->wValue.word;
-#ifdef NKRO_ENABLE
-                            if (!keyboard_protocol && keyboard_idle) {
-#else  /* NKRO_ENABLE */
-                            if (keyboard_idle) {
-#endif /* NKRO_ENABLE */
-                                /* arm the idle timer if boot protocol & idle */
-                                osalSysLockFromISR();
-                                chVTSetI(&keyboard_idle_timer, 4 * TIME_MS2I(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp);
-                                osalSysUnlockFromISR();
-                            }
                         }
                         usbSetupTransfer(usbp, NULL, 0, NULL);
                         return true;
-
                     case HID_REQ_SetIdle:
                         keyboard_idle = setup->wValue.hbyte;
-                        /* arm the timer */
-#ifdef NKRO_ENABLE
-                        if (!keymap_config.nkro && keyboard_idle) {
-#else  /* NKRO_ENABLE */
-                        if (keyboard_idle) {
-#endif /* NKRO_ENABLE */
-                            osalSysLockFromISR();
-                            chVTSetI(&keyboard_idle_timer, 4 * TIME_MS2I(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp);
-                            osalSysUnlockFromISR();
-                        }
-                        usbSetupTransfer(usbp, NULL, 0, NULL);
-                        return true;
+                        return usb_set_idle_cb(usbp);
                 }
                 break;
         }
@@ -719,52 +315,40 @@ static bool usb_requests_hook_cb(USBDriver *usbp) {
         return true;
     }
 
-    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 qmkusbRequestsHook(usbp);
+    for (int i = 0; i < USB_ENDPOINT_IN_COUNT; i++) {
+        if (usb_endpoints_in[i].usb_requests_cb != NULL) {
+            if (usb_endpoints_in[i].usb_requests_cb(usbp)) {
+                return true;
+            }
         }
     }
 
     return false;
 }
 
-static void usb_sof_cb(USBDriver *usbp) {
-    osalSysLockFromISR();
-    for (int i = 0; i < NUM_USB_DRIVERS; i++) {
-        qmkusbSOFHookI(&drivers.array[i].driver);
-    }
-    osalSysUnlockFromISR();
+static __attribute__((unused)) void dummy_cb(USBDriver *usbp) {
+    (void)usbp;
 }
 
-/* USB driver configuration */
 static const USBConfig usbcfg = {
     usb_event_cb,          /* USB events callback */
     usb_get_descriptor_cb, /* Device GET_DESCRIPTOR request callback */
     usb_requests_hook_cb,  /* Requests hook callback */
-    usb_sof_cb             /* Start Of Frame callback */
+#if STM32_USB_USE_OTG1 == TRUE || STM32_USB_USE_OTG2 == TRUE
+    dummy_cb, /* Workaround for OTG Peripherals not servicing new interrupts
+    after resuming from suspend. */
+#endif
 };
 
-/*
- * Initialize the USB driver
- */
 void init_usb_driver(USBDriver *usbp) {
-    for (int i = 0; i < NUM_USB_DRIVERS; i++) {
-#ifdef USB_ENDPOINTS_ARE_REORDERABLE
-        QMKUSBDriver *driver                       = &drivers.array[i].driver;
-        drivers.array[i].inout_ep_config.in_state  = &drivers.array[i].in_ep_state;
-        drivers.array[i].inout_ep_config.out_state = &drivers.array[i].out_ep_state;
-        drivers.array[i].int_ep_config.in_state    = &drivers.array[i].int_ep_state;
-        qmkusbObjectInit(driver, &drivers.array[i].config);
-        qmkusbStart(driver, &drivers.array[i].config);
-#else
-        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;
-        qmkusbObjectInit(driver, &drivers.array[i].config);
-        qmkusbStart(driver, &drivers.array[i].config);
-#endif
+    for (int i = 0; i < USB_ENDPOINT_IN_COUNT; i++) {
+        usb_endpoint_in_init(&usb_endpoints_in[i]);
+        usb_endpoint_in_start(&usb_endpoints_in[i]);
+    }
+
+    for (int i = 0; i < USB_ENDPOINT_OUT_COUNT; i++) {
+        usb_endpoint_out_init(&usb_endpoints_out[i]);
+        usb_endpoint_out_start(&usb_endpoints_out[i]);
     }
 
     /*
@@ -777,23 +361,31 @@ void init_usb_driver(USBDriver *usbp) {
     wait_ms(50);
     usbStart(usbp, &usbcfg);
     usbConnectBus(usbp);
-
-    chVTObjectInit(&keyboard_idle_timer);
 }
 
 __attribute__((weak)) void restart_usb_driver(USBDriver *usbp) {
     usbDisconnectBus(usbp);
     usbStop(usbp);
 
-#if USB_SUSPEND_WAKEUP_DELAY > 0
-    // Some hubs, kvm switches, and monitors do
-    // weird things, with USB device state bouncing
-    // around wildly on wakeup, yielding race
-    // conditions that can corrupt the keyboard state.
-    //
-    // Pause for a while to let things settle...
-    wait_ms(USB_SUSPEND_WAKEUP_DELAY);
-#endif
+    for (int i = 0; i < USB_ENDPOINT_IN_COUNT; i++) {
+        usb_endpoint_in_stop(&usb_endpoints_in[i]);
+    }
+
+    for (int i = 0; i < USB_ENDPOINT_OUT_COUNT; i++) {
+        usb_endpoint_out_stop(&usb_endpoints_out[i]);
+    }
+
+    wait_ms(50);
+
+    for (int i = 0; i < USB_ENDPOINT_IN_COUNT; i++) {
+        usb_endpoint_in_init(&usb_endpoints_in[i]);
+        usb_endpoint_in_start(&usb_endpoints_in[i]);
+    }
+
+    for (int i = 0; i < USB_ENDPOINT_OUT_COUNT; i++) {
+        usb_endpoint_out_init(&usb_endpoints_out[i]);
+        usb_endpoint_out_start(&usb_endpoints_out[i]);
+    }
 
     usbStart(usbp, &usbcfg);
     usbConnectBus(usbp);
@@ -804,81 +396,78 @@ __attribute__((weak)) void restart_usb_driver(USBDriver *usbp) {
  * ---------------------------------------------------------
  */
 
-/* Idle requests timer code
- * callback (called from ISR, unlocked state) */
-static void keyboard_idle_timer_cb(struct ch_virtual_timer *timer, void *arg) {
-    (void)timer;
-    USBDriver *usbp = (USBDriver *)arg;
-
-    osalSysLockFromISR();
-
-    /* check that the states of things are as they're supposed to */
-    if (usbGetDriverStateI(usbp) != USB_ACTIVE) {
-        /* do not rearm the timer, should be enabled on IDLE request */
-        osalSysUnlockFromISR();
-        return;
-    }
-
-#ifdef NKRO_ENABLE
-    if (!keymap_config.nkro && keyboard_idle && keyboard_protocol) {
-#else  /* NKRO_ENABLE */
-    if (keyboard_idle && keyboard_protocol) {
-#endif /* NKRO_ENABLE */
-        /* TODO: are we sure we want the KBD_ENDPOINT? */
-        if (!usbGetTransmitStatusI(usbp, KEYBOARD_IN_EPNUM)) {
-            usbStartTransmitI(usbp, KEYBOARD_IN_EPNUM, (uint8_t *)&keyboard_report_sent, KEYBOARD_EPSIZE);
-        }
-        /* rearm the timer */
-        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
-     * it should be enabled again on either IDLE or SET_PROTOCOL requests */
-    osalSysUnlockFromISR();
-}
-
 /* LED status */
 uint8_t keyboard_leds(void) {
     return keyboard_led_state;
 }
 
-void send_report(uint8_t endpoint, void *report, size_t size) {
-    osalSysLock();
-    if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) {
-        osalSysUnlock();
-        return;
-    }
-
-    if (usbGetTransmitStatusI(&USB_DRIVER, 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 */
-        if (osalThreadSuspendTimeoutS(&(&USB_DRIVER)->epc[endpoint]->in_state->thread, TIME_MS2I(10)) == MSG_TIMEOUT) {
-            osalSysUnlock();
-            return;
-        }
-    }
-    usbStartTransmitI(&USB_DRIVER, endpoint, report, size);
-    osalSysUnlock();
+/**
+ * @brief Send a report to the host, the report is enqueued into an output
+ * queue and send once the USB endpoint becomes empty.
+ *
+ * @param endpoint USB IN endpoint to send the report from
+ * @param report pointer to the report
+ * @param size size of the report
+ * @return true Success
+ * @return false Failure
+ */
+bool send_report(usb_endpoint_in_lut_t endpoint, void *report, size_t size) {
+    return usb_endpoint_in_send(&usb_endpoints_in[endpoint], (uint8_t *)report, size, TIME_MS2I(100), false);
+}
+
+/**
+ * @brief Send a report to the host, but delay the sending until the size of
+ * endpoint report is reached or the incompletely filled buffer is flushed with
+ * a call to `flush_report_buffered`. This is useful if the report is being
+ * updated frequently. The complete report is then enqueued into an output
+ * queue and send once the USB endpoint becomes empty.
+ *
+ * @param endpoint USB IN endpoint to send the report from
+ * @param report pointer to the report
+ * @param size size of the report
+ * @return true Success
+ * @return false Failure
+ */
+static bool send_report_buffered(usb_endpoint_in_lut_t endpoint, void *report, size_t size) {
+    return usb_endpoint_in_send(&usb_endpoints_in[endpoint], (uint8_t *)report, size, TIME_MS2I(100), true);
+}
+
+/** @brief Flush all buffered reports which were enqueued with a call to
+ * `send_report_buffered` that haven't been send. If necessary the buffered
+ * report can be padded with zeros up to the endpoints maximum size.
+ *
+ * @param endpoint USB IN endpoint to flush the reports from
+ * @param padded Pad the buffered report with zeros up to the endpoints maximum size
+ */
+static void flush_report_buffered(usb_endpoint_in_lut_t endpoint, bool padded) {
+    usb_endpoint_in_flush(&usb_endpoints_in[endpoint], padded);
+}
+
+/**
+ * @brief Receive a report from the host.
+ *
+ * @param endpoint USB OUT endpoint to receive the report from
+ * @param report pointer to the report
+ * @param size size of the report
+ * @return true Success
+ * @return false Failure
+ */
+static bool receive_report(usb_endpoint_out_lut_t endpoint, void *report, size_t size) {
+    return usb_endpoint_out_receive(&usb_endpoints_out[endpoint], (uint8_t *)report, size, TIME_IMMEDIATE);
 }
 
-/* prepare and start sending a report IN
- * not callable from ISR or locked state */
 void send_keyboard(report_keyboard_t *report) {
     /* If we're in Boot Protocol, don't send any report ID or other funky fields */
     if (!keyboard_protocol) {
-        send_report(KEYBOARD_IN_EPNUM, &report->mods, 8);
+        send_report(USB_ENDPOINT_IN_KEYBOARD, &report->mods, 8);
     } else {
-        send_report(KEYBOARD_IN_EPNUM, report, KEYBOARD_REPORT_SIZE);
+        send_report(USB_ENDPOINT_IN_KEYBOARD, report, KEYBOARD_REPORT_SIZE);
     }
-
-    keyboard_report_sent = *report;
 }
 
 void send_nkro(report_nkro_t *report) {
 #ifdef NKRO_ENABLE
-    send_report(SHARED_IN_EPNUM, report, sizeof(report_nkro_t));
+    send_report(USB_ENDPOINT_IN_SHARED, report, sizeof(report_nkro_t));
 #endif
 }
 
@@ -889,8 +478,7 @@ void send_nkro(report_nkro_t *report) {
 
 void send_mouse(report_mouse_t *report) {
 #ifdef MOUSE_ENABLE
-    send_report(MOUSE_IN_EPNUM, report, sizeof(report_mouse_t));
-    mouse_report_sent = *report;
+    send_report(USB_ENDPOINT_IN_MOUSE, report, sizeof(report_mouse_t));
 #endif
 }
 
@@ -901,25 +489,25 @@ void send_mouse(report_mouse_t *report) {
 
 void send_extra(report_extra_t *report) {
 #ifdef EXTRAKEY_ENABLE
-    send_report(SHARED_IN_EPNUM, report, sizeof(report_extra_t));
+    send_report(USB_ENDPOINT_IN_SHARED, report, sizeof(report_extra_t));
 #endif
 }
 
 void send_programmable_button(report_programmable_button_t *report) {
 #ifdef PROGRAMMABLE_BUTTON_ENABLE
-    send_report(SHARED_IN_EPNUM, report, sizeof(report_programmable_button_t));
+    send_report(USB_ENDPOINT_IN_SHARED, report, sizeof(report_programmable_button_t));
 #endif
 }
 
 void send_joystick(report_joystick_t *report) {
 #ifdef JOYSTICK_ENABLE
-    send_report(JOYSTICK_IN_EPNUM, report, sizeof(report_joystick_t));
+    send_report(USB_ENDPOINT_IN_JOYSTICK, report, sizeof(report_joystick_t));
 #endif
 }
 
 void send_digitizer(report_digitizer_t *report) {
 #ifdef DIGITIZER_ENABLE
-    send_report(DIGITIZER_IN_EPNUM, report, sizeof(report_digitizer_t));
+    send_report(USB_ENDPOINT_IN_DIGITIZER, report, sizeof(report_digitizer_t));
 #endif
 }
 
@@ -931,46 +519,21 @@ void send_digitizer(report_digitizer_t *report) {
 #ifdef CONSOLE_ENABLE
 
 int8_t sendchar(uint8_t c) {
-    rbuf_enqueue(c);
-    return 0;
+    return (int8_t)send_report_buffered(USB_ENDPOINT_IN_CONSOLE, &c, sizeof(uint8_t));
 }
 
 void console_task(void) {
-    if (!rbuf_has_data()) {
-        return;
-    }
-
-    osalSysLock();
-    if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) {
-        osalSysUnlock();
-        return;
-    }
-
-    if (usbGetTransmitStatusI(&USB_DRIVER, CONSOLE_IN_EPNUM)) {
-        osalSysUnlock();
-        return;
-    }
-
-    // Send in chunks - padded with zeros to 32
-    char    send_buf[CONSOLE_EPSIZE] = {0};
-    uint8_t send_buf_count           = 0;
-    while (rbuf_has_data() && send_buf_count < CONSOLE_EPSIZE) {
-        send_buf[send_buf_count++] = rbuf_dequeue();
-    }
-
-    usbStartTransmitI(&USB_DRIVER, CONSOLE_IN_EPNUM, (const uint8_t *)send_buf, CONSOLE_EPSIZE);
-    osalSysUnlock();
+    flush_report_buffered(USB_ENDPOINT_IN_CONSOLE, true);
 }
 
 #endif /* CONSOLE_ENABLE */
 
 #ifdef RAW_ENABLE
 void raw_hid_send(uint8_t *data, uint8_t length) {
-    // TODO: implement variable size packet
     if (length != RAW_EPSIZE) {
         return;
     }
-    chnWrite(&drivers.raw_driver.driver, data, length);
+    send_report(USB_ENDPOINT_IN_RAW, data, length);
 }
 
 __attribute__((weak)) void raw_hid_receive(uint8_t *data, uint8_t length) {
@@ -981,13 +544,9 @@ __attribute__((weak)) void raw_hid_receive(uint8_t *data, uint8_t length) {
 
 void raw_hid_task(void) {
     uint8_t buffer[RAW_EPSIZE];
-    size_t  size = 0;
-    do {
-        size = chnReadTimeout(&drivers.raw_driver.driver, buffer, sizeof(buffer), TIME_IMMEDIATE);
-        if (size > 0) {
-            raw_hid_receive(buffer, size);
-        }
-    } while (size > 0);
+    while (receive_report(USB_ENDPOINT_OUT_RAW, buffer, sizeof(buffer))) {
+        raw_hid_receive(buffer, sizeof(buffer));
+    }
 }
 
 #endif
@@ -995,32 +554,59 @@ void raw_hid_task(void) {
 #ifdef MIDI_ENABLE
 
 void send_midi_packet(MIDI_EventPacket_t *event) {
-    chnWrite(&drivers.midi_driver.driver, (uint8_t *)event, sizeof(MIDI_EventPacket_t));
+    send_report(USB_ENDPOINT_IN_MIDI, (uint8_t *)event, sizeof(MIDI_EventPacket_t));
 }
 
 bool recv_midi_packet(MIDI_EventPacket_t *const event) {
-    size_t size = chnReadTimeout(&drivers.midi_driver.driver, (uint8_t *)event, sizeof(MIDI_EventPacket_t), TIME_IMMEDIATE);
-    return size == sizeof(MIDI_EventPacket_t);
+    return receive_report(USB_ENDPOINT_OUT_MIDI, (uint8_t *)event, sizeof(MIDI_EventPacket_t));
 }
+
 void midi_ep_task(void) {
     uint8_t buffer[MIDI_STREAM_EPSIZE];
-    size_t  size = 0;
-    do {
-        size = chnReadTimeout(&drivers.midi_driver.driver, buffer, sizeof(buffer), TIME_IMMEDIATE);
-        if (size > 0) {
-            MIDI_EventPacket_t event;
-            recv_midi_packet(&event);
-        }
-    } while (size > 0);
+    while (receive_report(USB_ENDPOINT_OUT_MIDI, buffer, sizeof(buffer))) {
+        MIDI_EventPacket_t event;
+        // TODO: this seems totally wrong? The midi task will never see any
+        // packets if we consume them here
+        recv_midi_packet(&event);
+    }
 }
 #endif
 
 #ifdef VIRTSER_ENABLE
 
+#    include "hal_usb_cdc.h"
+/**
+ * @brief CDC serial driver configuration structure. Set to 9600 baud, 1 stop bit, no parity, 8 data bits.
+ */
+static cdc_linecoding_t linecoding = {{0x00, 0x96, 0x00, 0x00}, LC_STOP_1, LC_PARITY_NONE, 8};
+
+bool virtser_usb_request_cb(USBDriver *usbp) {
+    if ((usbp->setup[0] & USB_RTYPE_TYPE_MASK) == USB_RTYPE_TYPE_CLASS) { /* bmRequestType */
+        if (usbp->setup[4] == CCI_INTERFACE) {                            /* wIndex (LSB) */
+            switch (usbp->setup[1]) {                                     /* bRequest */
+                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;
+}
+
 void virtser_init(void) {}
 
 void virtser_send(const uint8_t byte) {
-    chnWrite(&drivers.serial_driver.driver, &byte, 1);
+    send_report_buffered(USB_ENDPOINT_IN_CDC_DATA, (void *)&byte, sizeof(byte));
 }
 
 __attribute__((weak)) void virtser_recv(uint8_t c) {
@@ -1028,14 +614,14 @@ __attribute__((weak)) void virtser_recv(uint8_t c) {
 }
 
 void virtser_task(void) {
-    uint8_t numBytesReceived = 0;
-    uint8_t buffer[16];
-    do {
-        numBytesReceived = chnReadTimeout(&drivers.serial_driver.driver, buffer, sizeof(buffer), TIME_IMMEDIATE);
-        for (int i = 0; i < numBytesReceived; i++) {
+    uint8_t buffer[CDC_EPSIZE];
+    while (receive_report(USB_ENDPOINT_OUT_CDC_DATA, buffer, sizeof(buffer))) {
+        for (int i = 0; i < sizeof(buffer); i++) {
             virtser_recv(buffer[i]);
         }
-    } while (numBytesReceived > 0);
+    }
+
+    flush_report_buffered(USB_ENDPOINT_IN_CDC_DATA, false);
 }
 
 #endif
diff --git a/tmk_core/protocol/chibios/usb_main.h b/tmk_core/protocol/chibios/usb_main.h
index 3fd1e84fe8..5ba6fb1961 100644
--- a/tmk_core/protocol/chibios/usb_main.h
+++ b/tmk_core/protocol/chibios/usb_main.h
@@ -1,25 +1,21 @@
-/*
- * (c) 2015 flabberast <s3+flabbergast@sdfeu.org>
- *
- * Based on the following work:
- *  - Guillaume Duc's raw hid example (MIT License)
- *    https://github.com/guiduc/usb-hid-chibios-example
- *  - PJRC Teensy examples (MIT License)
- *    https://www.pjrc.com/teensy/usb_keyboard.html
- *  - hasu's TMK keyboard code (GPL v2 and some code Modified BSD)
- *    https://github.com/tmk/tmk_keyboard/
- *  - ChibiOS demo code (Apache 2.0 License)
- *    http://www.chibios.org
- *
- * Since some GPL'd code is used, this work is licensed under
- * GPL v2 or later.
- */
+// Copyright 2023 Stefan Kerkmann (@KarlK90)
+// Copyright 2020 Ryan (@fauxpark)
+// Copyright 2020 Joel Challis (@zvecr)
+// Copyright 2018 James Laird-Wah
+// Copyright 2016 Fredizzimo
+// Copyright 2016 Giovanni Di Sirio
+// SPDX-License-Identifier: GPL-3.0-or-later OR Apache-2.0
 
 #pragma once
 
 #include <ch.h>
 #include <hal.h>
 
+#include "usb_device_state.h"
+#include "usb_descriptor.h"
+#include "usb_driver.h"
+#include "usb_endpoints.h"
+
 /* -------------------------
  * General USB driver header
  * -------------------------
@@ -36,6 +32,8 @@ void init_usb_driver(USBDriver *usbp);
 /* Restart the USB driver and bus */
 void restart_usb_driver(USBDriver *usbp);
 
+bool send_report(usb_endpoint_in_lut_t endpoint, void *report, size_t size);
+
 /* ---------------
  * USB Event queue
  * ---------------
@@ -58,3 +56,14 @@ void usb_event_queue_task(void);
 int8_t sendchar(uint8_t c);
 
 #endif /* CONSOLE_ENABLE */
+
+/* --------------
+ * Virtser header
+ * --------------
+ */
+
+#if defined(VIRTSER_ENABLE)
+
+bool virtser_usb_request_cb(USBDriver *usbp);
+
+#endif
diff --git a/tmk_core/protocol/chibios/usb_report_handling.c b/tmk_core/protocol/chibios/usb_report_handling.c
new file mode 100644
index 0000000000..64074b2164
--- /dev/null
+++ b/tmk_core/protocol/chibios/usb_report_handling.c
@@ -0,0 +1,296 @@
+// Copyright 2023 Stefan Kerkmann (@KarlK90)
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "usb_report_handling.h"
+#include "usb_endpoints.h"
+#include "usb_main.h"
+#include "usb_types.h"
+#include "usb_driver.h"
+#include "report.h"
+
+extern usb_endpoint_in_t     usb_endpoints_in[USB_ENDPOINT_IN_COUNT];
+extern usb_endpoint_in_lut_t usb_endpoint_interface_lut[TOTAL_INTERFACES];
+
+void usb_set_report(usb_fs_report_t **reports, const uint8_t *data, size_t length) {
+    if (*reports == NULL) {
+        return;
+    }
+
+    (*reports)->last_report = chVTGetSystemTimeX();
+    (*reports)->length      = length;
+    memcpy(&(*reports)->data, data, length);
+}
+
+void usb_get_report(usb_fs_report_t **reports, uint8_t report_id, usb_fs_report_t *report) {
+    (void)report_id;
+    if (*reports == NULL) {
+        return;
+    }
+
+    report->length = (*reports)->length;
+    memcpy(&report->data, &(*reports)->data, report->length);
+}
+
+void usb_reset_report(usb_fs_report_t **reports) {
+    if (*reports == NULL) {
+        return;
+    }
+
+    memset(&(*reports)->data, 0, (*reports)->length);
+    (*reports)->idle_rate   = 0;
+    (*reports)->last_report = 0;
+}
+
+void usb_shared_set_report(usb_fs_report_t **reports, const uint8_t *data, size_t length) {
+    uint8_t report_id = data[0];
+
+    if (report_id > REPORT_ID_COUNT || reports[report_id] == NULL) {
+        return;
+    }
+
+    reports[report_id]->last_report = chVTGetSystemTimeX();
+    reports[report_id]->length      = length;
+    memcpy(&reports[report_id]->data, data, length);
+}
+
+void usb_shared_get_report(usb_fs_report_t **reports, uint8_t report_id, usb_fs_report_t *report) {
+    if (report_id > REPORT_ID_COUNT || reports[report_id] == NULL) {
+        return;
+    }
+
+    report->length = reports[report_id]->length;
+    memcpy(&report->data, &reports[report_id]->data, report->length);
+}
+
+void usb_shared_reset_report(usb_fs_report_t **reports) {
+    for (int i = 0; i <= REPORT_ID_COUNT; i++) {
+        if (reports[i] == NULL) {
+            continue;
+        }
+        memset(&reports[i]->data, 0, reports[i]->length);
+        reports[i]->idle_rate   = 0;
+        reports[i]->last_report = 0;
+    }
+}
+
+bool usb_get_report_cb(USBDriver *driver) {
+    usb_control_request_t *setup     = (usb_control_request_t *)driver->setup;
+    uint8_t                interface = setup->wIndex;
+    uint8_t                report_id = setup->wValue.lbyte;
+
+    static usb_fs_report_t report;
+
+    if (!IS_VALID_INTERFACE(interface) || !IS_VALID_REPORT_ID(report_id)) {
+        return false;
+    }
+
+    usb_endpoint_in_lut_t ep = usb_endpoint_interface_lut[interface];
+
+    if (!IS_VALID_USB_ENDPOINT_IN_LUT(ep)) {
+        return false;
+    }
+
+    usb_report_storage_t *report_storage = usb_endpoints_in[ep].report_storage;
+
+    if (report_storage == NULL) {
+        return false;
+    }
+
+    report_storage->get_report(report_storage->reports, report_id, &report);
+
+    usbSetupTransfer(driver, (uint8_t *)report.data, report.length, NULL);
+
+    return true;
+}
+
+static bool run_idle_task = false;
+
+void usb_set_idle_rate(usb_fs_report_t **reports, uint8_t report_id, uint8_t idle_rate) {
+    (void)report_id;
+
+    if (*reports == NULL) {
+        return;
+    }
+
+    (*reports)->idle_rate = idle_rate * 4;
+
+    run_idle_task |= idle_rate != 0;
+}
+
+uint8_t usb_get_idle_rate(usb_fs_report_t **reports, uint8_t report_id) {
+    (void)report_id;
+
+    if (*reports == NULL) {
+        return 0;
+    }
+
+    return (*reports)->idle_rate / 4;
+}
+
+bool usb_idle_timer_elapsed(usb_fs_report_t **reports, uint8_t report_id) {
+    (void)report_id;
+
+    if (*reports == NULL) {
+        return false;
+    }
+
+    osalSysLock();
+    time_msecs_t idle_rate   = (*reports)->idle_rate;
+    systime_t    last_report = (*reports)->last_report;
+    osalSysUnlock();
+
+    if (idle_rate == 0) {
+        return false;
+    }
+
+    return chTimeI2MS(chVTTimeElapsedSinceX(last_report)) >= idle_rate;
+}
+
+void usb_shared_set_idle_rate(usb_fs_report_t **reports, uint8_t report_id, uint8_t idle_rate) {
+    // USB spec demands that a report_id of 0 would set the idle rate for all
+    // reports of that endpoint, but this can easily lead to resource
+    // exhaustion, therefore we deliberalty break the spec at this point.
+    if (report_id == 0 || report_id > REPORT_ID_COUNT || reports[report_id] == NULL) {
+        return;
+    }
+
+    reports[report_id]->idle_rate = idle_rate * 4;
+
+    run_idle_task |= idle_rate != 0;
+}
+
+uint8_t usb_shared_get_idle_rate(usb_fs_report_t **reports, uint8_t report_id) {
+    if (report_id > REPORT_ID_COUNT || reports[report_id] == NULL) {
+        return 0;
+    }
+
+    return reports[report_id]->idle_rate / 4;
+}
+
+bool usb_shared_idle_timer_elapsed(usb_fs_report_t **reports, uint8_t report_id) {
+    if (report_id > REPORT_ID_COUNT || reports[report_id] == NULL) {
+        return false;
+    }
+
+    osalSysLock();
+    time_msecs_t idle_rate   = reports[report_id]->idle_rate;
+    systime_t    last_report = reports[report_id]->last_report;
+    osalSysUnlock();
+
+    if (idle_rate == 0) {
+        return false;
+    }
+
+    return chTimeI2MS(chVTTimeElapsedSinceX(last_report)) >= idle_rate;
+}
+
+void usb_idle_task(void) {
+    if (!run_idle_task) {
+        return;
+    }
+
+    static usb_fs_report_t report;
+    bool                   non_zero_idle_rate_found = false;
+
+    for (int ep = 0; ep < USB_ENDPOINT_IN_COUNT; ep++) {
+        usb_report_storage_t *report_storage = usb_endpoints_in[ep].report_storage;
+
+        if (report_storage == NULL) {
+            continue;
+        }
+
+#if defined(SHARED_EP_ENABLE)
+        if (ep == USB_ENDPOINT_IN_SHARED) {
+            for (int report_id = 1; report_id <= REPORT_ID_COUNT; report_id++) {
+                osalSysLock();
+                non_zero_idle_rate_found |= report_storage->get_idle(report_storage->reports, report_id) != 0;
+                osalSysUnlock();
+
+                if (usb_endpoint_in_is_inactive(&usb_endpoints_in[ep]) && report_storage->idle_timer_elasped(report_storage->reports, report_id)) {
+                    osalSysLock();
+                    report_storage->get_report(report_storage->reports, report_id, &report);
+                    osalSysUnlock();
+                    send_report(ep, &report.data, report.length);
+                }
+            }
+            continue;
+        }
+#endif
+
+        osalSysLock();
+        non_zero_idle_rate_found |= report_storage->get_idle(report_storage->reports, 0) != 0;
+        osalSysUnlock();
+
+        if (usb_endpoint_in_is_inactive(&usb_endpoints_in[ep]) && report_storage->idle_timer_elasped(report_storage->reports, 0)) {
+            osalSysLock();
+            report_storage->get_report(report_storage->reports, 0, &report);
+            osalSysUnlock();
+            send_report(ep, &report.data, report.length);
+        }
+    }
+
+    run_idle_task = non_zero_idle_rate_found;
+}
+
+bool usb_get_idle_cb(USBDriver *driver) {
+    usb_control_request_t *setup     = (usb_control_request_t *)driver->setup;
+    uint8_t                interface = setup->wIndex;
+    uint8_t                report_id = setup->wValue.lbyte;
+
+    static uint8_t _Alignas(4) idle_rate;
+
+    if (!IS_VALID_INTERFACE(interface) || !IS_VALID_REPORT_ID(report_id)) {
+        return false;
+    }
+
+    usb_endpoint_in_lut_t ep = usb_endpoint_interface_lut[interface];
+
+    if (!IS_VALID_USB_ENDPOINT_IN_LUT(ep)) {
+        return false;
+    }
+
+    usb_report_storage_t *report_storage = usb_endpoints_in[ep].report_storage;
+
+    if (report_storage == NULL) {
+        return false;
+    }
+
+    idle_rate = report_storage->get_idle(report_storage->reports, report_id);
+
+    usbSetupTransfer(driver, &idle_rate, 1, NULL);
+
+    return true;
+}
+
+bool usb_set_idle_cb(USBDriver *driver) {
+    usb_control_request_t *setup     = (usb_control_request_t *)driver->setup;
+    uint8_t                interface = setup->wIndex;
+    uint8_t                report_id = setup->wValue.lbyte;
+    uint8_t                idle_rate = setup->wValue.hbyte;
+
+    if (!IS_VALID_INTERFACE(interface) || !IS_VALID_REPORT_ID(report_id)) {
+        return false;
+    }
+
+    usb_endpoint_in_lut_t ep = usb_endpoint_interface_lut[interface];
+
+    if (!IS_VALID_USB_ENDPOINT_IN_LUT(ep)) {
+        return false;
+    }
+
+    usb_report_storage_t *report_storage = usb_endpoints_in[ep].report_storage;
+
+    if (report_storage == NULL) {
+        return false;
+    }
+
+    report_storage->set_idle(report_storage->reports, report_id, idle_rate);
+
+    usbSetupTransfer(driver, NULL, 0, NULL);
+
+    return true;
+}
diff --git a/tmk_core/protocol/chibios/usb_report_handling.h b/tmk_core/protocol/chibios/usb_report_handling.h
new file mode 100644
index 0000000000..18b4ce5e26
--- /dev/null
+++ b/tmk_core/protocol/chibios/usb_report_handling.h
@@ -0,0 +1,77 @@
+// Copyright 2023 Stefan Kerkmann (@KarlK90)
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <ch.h>
+#include <hal.h>
+#include <stdint.h>
+
+#include "timer.h"
+
+typedef struct {
+    time_msecs_t idle_rate;
+    systime_t    last_report;
+    uint8_t      data[64];
+    size_t       length;
+} usb_fs_report_t;
+
+typedef struct {
+    usb_fs_report_t **reports;
+    const void (*get_report)(usb_fs_report_t **, uint8_t, usb_fs_report_t *);
+    const void (*set_report)(usb_fs_report_t **, const uint8_t *, size_t);
+    const void (*reset_report)(usb_fs_report_t **);
+    const void (*set_idle)(usb_fs_report_t **, uint8_t, uint8_t);
+    const uint8_t (*get_idle)(usb_fs_report_t **, uint8_t);
+    const bool (*idle_timer_elasped)(usb_fs_report_t **, uint8_t);
+} usb_report_storage_t;
+
+#define QMK_USB_REPORT_STROAGE_ENTRY(_report_id, _report_size) [_report_id] = &((usb_fs_report_t){.data = {[0] = _report_id}, .length = _report_size})
+
+#define QMK_USB_REPORT_STORAGE(_get_report, _set_report, _reset_report, _get_idle, _set_idle, _idle_timer_elasped, _report_count, _reports...) \
+    &((usb_report_storage_t){                                                                                                                  \
+        .reports            = (_Alignas(4) usb_fs_report_t *[_report_count]){_reports},                                                        \
+        .get_report         = _get_report,                                                                                                     \
+        .set_report         = _set_report,                                                                                                     \
+        .reset_report       = _reset_report,                                                                                                   \
+        .get_idle           = _get_idle,                                                                                                       \
+        .set_idle           = _set_idle,                                                                                                       \
+        .idle_timer_elasped = _idle_timer_elasped,                                                                                             \
+    })
+
+#define QMK_USB_REPORT_STORAGE_DEFAULT(_report_length)                        \
+    QMK_USB_REPORT_STORAGE(&usb_get_report,         /* _get_report */         \
+                           &usb_set_report,         /* _set_report */         \
+                           &usb_reset_report,       /* _reset_report */       \
+                           &usb_get_idle_rate,      /* _get_idle */           \
+                           &usb_set_idle_rate,      /* _set_idle */           \
+                           &usb_idle_timer_elapsed, /* _idle_timer_elasped */ \
+                           1,                       /* _report_count */       \
+                           QMK_USB_REPORT_STROAGE_ENTRY(0, _report_length))
+
+// USB HID SET_REPORT and GET_REPORT  handling functions
+void usb_set_report(usb_fs_report_t **reports, const uint8_t *data, size_t length);
+void usb_shared_set_report(usb_fs_report_t **reports, const uint8_t *data, size_t length);
+
+void usb_get_report(usb_fs_report_t **reports, uint8_t report_id, usb_fs_report_t *report);
+void usb_shared_get_report(usb_fs_report_t **reports, uint8_t report_id, usb_fs_report_t *report);
+
+void usb_reset_report(usb_fs_report_t **reports);
+void usb_shared_reset_report(usb_fs_report_t **reports);
+
+bool usb_get_report_cb(USBDriver *driver);
+
+// USB HID SET_IDLE and GET_IDLE handling functions
+void usb_idle_task(void);
+
+void usb_set_idle_rate(usb_fs_report_t **timers, uint8_t report_id, uint8_t idle_rate);
+void usb_shared_set_idle_rate(usb_fs_report_t **timers, uint8_t report_id, uint8_t idle_rate);
+
+uint8_t usb_get_idle_rate(usb_fs_report_t **timers, uint8_t report_id);
+uint8_t usb_shared_get_idle_rate(usb_fs_report_t **timers, uint8_t report_id);
+
+bool usb_idle_timer_elapsed(usb_fs_report_t **timers, uint8_t report_id);
+bool usb_shared_idle_timer_elapsed(usb_fs_report_t **timers, uint8_t report_id);
+
+bool usb_get_idle_cb(USBDriver *driver);
+bool usb_set_idle_cb(USBDriver *driver);
diff --git a/tmk_core/protocol/report.h b/tmk_core/protocol/report.h
index 47bc4f2f2b..0e4f6e9def 100644
--- a/tmk_core/protocol/report.h
+++ b/tmk_core/protocol/report.h
@@ -29,7 +29,8 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 // clang-format off
 
 /* HID report IDs */
-enum hid_report_ids {
+enum hid_report_ids { 
+    REPORT_ID_ALL = 0,
     REPORT_ID_KEYBOARD = 1,
     REPORT_ID_MOUSE,
     REPORT_ID_SYSTEM,
@@ -37,9 +38,12 @@ enum hid_report_ids {
     REPORT_ID_PROGRAMMABLE_BUTTON,
     REPORT_ID_NKRO,
     REPORT_ID_JOYSTICK,
-    REPORT_ID_DIGITIZER
+    REPORT_ID_DIGITIZER,
+    REPORT_ID_COUNT = REPORT_ID_DIGITIZER
 };
 
+#define IS_VALID_REPORT_ID(id) ((id) >= REPORT_ID_ALL && (id) <= REPORT_ID_COUNT)
+
 /* Mouse buttons */
 #define MOUSE_BTN_MASK(n) (1 << (n))
 enum mouse_buttons {
diff --git a/tmk_core/protocol/usb_descriptor.h b/tmk_core/protocol/usb_descriptor.h
index 2469990f4d..ecfb022702 100644
--- a/tmk_core/protocol/usb_descriptor.h
+++ b/tmk_core/protocol/usb_descriptor.h
@@ -196,6 +196,8 @@ enum usb_interfaces {
     TOTAL_INTERFACES
 };
 
+#define IS_VALID_INTERFACE(i) ((i) >= 0 && (i) < TOTAL_INTERFACES)
+
 #define NEXT_EPNUM __COUNTER__
 
 /*