usb: allow more flexible endpoint allocation

rockbox-gerrit-noreply--- via rockbox-cvs <[email protected]> Mon, 4 May 2026 13:31:21 -0400
Newsgroups gmane.comp.systems.archos.rockbox.cvs
Message-ID <[email protected]>
commit 41caf678fe6324b284a2df94900ec5a78a55c5d6
Author: mojyack <[email protected]>
Date:   Sun Dec 28 15:48:53 2025 +0900

    usb: allow more flexible endpoint allocation
    
    the capabilities of endpoint of several devices such as dwc2 change during
    runtime, so they cannot be determined during driver initialization.
    therefore, allocation using ep_specs is inappropriate.
    
    to support these devices, add functions to the driver that determine whether
    endpoints are available and make allocation more flexible.
    
    tested with ipodvideo(arc) and erosqnative(designware)
    
    Change-Id: I8005c17f3d763cd17306bf49918e1cd8084bdeff

diff --git a/firmware/drivers/isp1583.c b/firmware/drivers/isp1583.c
index 39b9d1190e..93aba17a7b 100644
--- a/firmware/drivers/isp1583.c
+++ b/firmware/drivers/isp1583.c
@@ -626,18 +626,17 @@ void usb_drv_cancel_all_transfers(void)
         endpoints[i].halt[0] = endpoints[i].halt[1] = 1;
 }
 
-int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size)
+void usb_drv_ep_init(const struct usb_drv_ep_alloc_ctx* ctx, int ep)
 {
-    (void)max_packet_size; /* FIXME: support max packet size override */
-    (void)type;
-    (void)endpoint;
-    return 0;
+    /* FIXME: support max packet size override */
+    (void)ctx;
+    (void)ep;
 }
 
-int usb_drv_deinit_endpoint(int endpoint)
+void usb_drv_ep_deinit(const struct usb_drv_ep_alloc_ctx* ctx, int ep)
 {
-    (void)endpoint;
-    return 0;
+    (void)ctx;
+    (void)ep;
 }
 
 static void bus_reset(void)
diff --git a/firmware/drivers/m66591.c b/firmware/drivers/m66591.c
index cf41aced31..6095e085e3 100644
--- a/firmware/drivers/m66591.c
+++ b/firmware/drivers/m66591.c
@@ -640,8 +640,11 @@ void usb_drv_set_test_mode(int mode) {
     M66591_TESTMODE |= mode;
 }
 
-int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size) {
-    (void)max_packet_size; /* FIXME: support max packet size override */
+void usb_drv_ep_init(const struct usb_drv_ep_alloc_ctx* ctx, int ep) {
+    /* FIXME: support max packet size override */
+    const int epnum = EP_NUM(ep);
+    const int epdir = EP_DIR(ep);
+    const int type = ctx->type[epnum][epdir];
 
     int pipecfg = 0;
 
@@ -651,41 +654,38 @@ int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size) {
     } else if(type == USB_ENDPOINT_XFER_BULK) {
         pipecfg |= 1<<13;
     } else {
-        /* Not a supported type */
-        return -1;
+        panicf("mxx: unsupported type %d", type);
     }
 
-    int num = endpoint & USB_ENDPOINT_NUMBER_MASK;
-    int dir = endpoint & USB_ENDPOINT_DIR_MASK;
-    if (dir == USB_DIR_IN) {
+    if (epdir == DIR_IN) {
         pipecfg |= (1<<4);
     }
 
-    M66591_eps[num].dir = dir;
+    M66591_eps[epnum].dir = epdir == DIR_IN ? USB_DIR_IN : USB_DIR_OUT;
 
-    M66591_PIPE_CFGSEL=num;
+    M66591_PIPE_CFGSEL = epnum;
 
     /* Enable pipe (15) */
     pipecfg |= 1<<15;
 
-    pipe_handshake(num, PIPE_SHAKE_NAK);
+    pipe_handshake(epnum, PIPE_SHAKE_NAK);
 
     /* Setup the flags */
     M66591_PIPE_CFGWND=pipecfg;
 
-    pipe_init(num);
+    pipe_init(epnum);
 
-    logf("mxx: ep req ep#: %d config: 0x%04x", num, M66591_PIPE_CFGWND);
-
-    return 0;
+    logf("mxx: ep req ep#: %d config: 0x%04x", epnum, M66591_PIPE_CFGWND);
 }
 
 /* Used by stack to tell the helper functions that the pipe is not in use */
-int usb_drv_deinit_endpoint(int endpoint) {
-    int num = endpoint & USB_ENDPOINT_NUMBER_MASK;
+void usb_drv_ep_deinit(const struct usb_drv_ep_alloc_ctx* ctx, int ep) {
+    (void)ctx;
+
+    int num = ep & USB_ENDPOINT_NUMBER_MASK;
 
     if (num < 1 || num > USB_NUM_ENDPOINTS) {
-        return -1;
+        return;
     }
 
     int flags = disable_irq_save();
@@ -695,8 +695,6 @@ int usb_drv_deinit_endpoint(int endpoint) {
     M66591_eps[num].dir = -1;
 
     restore_irq(flags);
-
-    return 0;
 }
 
 /* Periodically called to check if a cable was plugged into the device */
diff --git a/firmware/drivers/usb-designware.c b/firmware/drivers/usb-designware.c
index d1274f5e41..61fa4acc70 100644
--- a/firmware/drivers/usb-designware.c
+++ b/firmware/drivers/usb-designware.c
@@ -24,6 +24,8 @@
 #include <inttypes.h>
 #include <string.h>
 
+#include "usb-designware.h"
+
 #include "config.h"
 #include "cpu.h"
 #include "system.h"
@@ -35,8 +37,6 @@
 #include "usb_ch9.h"
 #include "usb_core.h"
 
-#include "usb-designware.h"
-
 /* Define LOGF_ENABLE to enable logf output in this file */
 /*#define LOGF_ENABLE*/
 #include "logf.h"
@@ -155,9 +155,6 @@ struct usb_dw_ep0
     struct usb_ctrlrequest pending_req;
 };
 
-struct usb_drv_ep_spec usb_drv_ep_specs[USB_NUM_ENDPOINTS]; /* filled in usb_drv_init */
-uint8_t usb_drv_ep_specs_flags = 0;
-
 static const char* const dw_dir_str[USB_DW_NUM_DIRS] =
 {
     [USB_DW_EPDIR_IN]  = "IN",
@@ -195,7 +192,6 @@ static uint32_t usb_endpoints;  /* available EPs mask */
    (usually 1), otherwise it is the number of dedicated Tx FIFOs
    (not counting NPTX FIFO that is always dedicated for IN0). */
 static int n_ptxfifos;
-static uint16_t ptxfifo_usage;
 
 static uint32_t hw_maxbytes;
 static uint32_t hw_maxpackets;
@@ -672,47 +668,28 @@ static void usb_dw_unconfigure_ep(int epnum, enum usb_dw_epdir epdir)
 #endif
         ep_periodic_msk &= ~(1 << epnum);
 #endif
-        ptxfifo_usage &= ~(1 << GET_DTXFNUM(epnum));
     }
 
     usb_dw_flush_endpoint(epnum, epdir);
     DWC_EPCTL(epnum, epdir) = epctl;
 }
 
-static int usb_dw_configure_ep(int epnum,
-                enum usb_dw_epdir epdir, int type, int maxpktsize)
+static void usb_dw_configure_ep(const struct usb_drv_ep_alloc_ctx* ctx, int epnum, enum usb_dw_epdir epdir, int type, int maxpktsize)
 {
     uint32_t epctl = SETD0PIDEF|EPTYP(type)|USBAEP|maxpktsize;
 
-    if (epdir == USB_DW_EPDIR_IN)
+    if (epdir == USB_DW_EPDIR_IN && ctx->assigned_txfifos[epnum] > 0)
     {
-        /*
-         * If the hardware has dedicated fifos, we must give each
-         * IN EP a unique tx-fifo even if it is non-periodic.
-         */
 #ifdef USB_DW_SHARED_FIFO
+        ep_periodic_msk |= (1 << epnum);
 #ifndef USB_DW_ARCH_SLAVE
         epctl |= DWC_DIEPCTL(epnum) & NEXTEP(0xf);
 #endif
-        if (type == USB_ENDPOINT_XFER_INT)
-#endif
-        {
-            int fnum;
-            for (fnum = 1; fnum <= n_ptxfifos; fnum++)
-                if (~ptxfifo_usage & (1 << fnum))
-                    break;
-            if (fnum > n_ptxfifos)
-                return -1; /* no available fifos */
-            ptxfifo_usage |= (1 << fnum);
-            epctl |= DTXFNUM(fnum);
-#ifdef USB_DW_SHARED_FIFO
-            ep_periodic_msk |= (1 << epnum);
 #endif
-        }
+        epctl |= DTXFNUM(ctx->assigned_txfifos[epnum]);
     }
 
     DWC_EPCTL(epnum, epdir) = epctl;
-    return 0; /* ok */
 }
 
 static void usb_dw_reset_endpoints(void)
@@ -753,7 +730,6 @@ static void usb_dw_reset_endpoints(void)
             usb_dw_unconfigure_ep(ep, USB_DW_EPDIR_IN);
     }
 
-    ptxfifo_usage = 0;
 #ifdef USB_DW_SHARED_FIFO
     ep_periodic_msk = 0;
 #endif
@@ -1509,17 +1485,6 @@ static void usb_dw_init(void)
     /* Soft reconnect */
     udelay(3000);
     DWC_DCTL &= ~SDIS;
-
-    /* Fill endpoint spec table FIXME: should be done in usb_drv_startup() */
-    usb_drv_ep_specs[0].type[DIR_OUT] = USB_ENDPOINT_XFER_CONTROL;
-    usb_drv_ep_specs[0].type[DIR_IN] = USB_ENDPOINT_XFER_CONTROL;
-    for(int i = 1; i < USB_NUM_ENDPOINTS; i += 1) {
-        bool out_avail = usb_endpoints & (1 << (i + USB_DW_DIR_OFF(USB_DW_EPDIR_OUT)));
-        usb_drv_ep_specs[i].type[DIR_OUT] = out_avail ? USB_ENDPOINT_TYPE_ANY : USB_ENDPOINT_TYPE_NONE;
-
-        bool in_avail = usb_endpoints & (1 << (i + USB_DW_DIR_OFF(USB_DW_EPDIR_IN)));
-        usb_drv_ep_specs[i].type[DIR_IN] = in_avail ? USB_ENDPOINT_TYPE_ANY : USB_ENDPOINT_TYPE_NONE;
-    }
 }
 
 static void usb_dw_exit(void)
@@ -1617,12 +1582,65 @@ void INT_USB_FUNC(void)
     usb_dw_irq();
 }
 
-int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size)
+void usb_drv_ep_reset_alloc_ctx(struct usb_drv_ep_alloc_ctx* ctx)
+{
+    memset(ctx, 0, sizeof(*ctx));
+}
+
+bool usb_drv_ep_allocate(struct usb_drv_ep_alloc_ctx* ctx, int ep, int type, int max_packet_size)
 {
     (void)max_packet_size; /* FIXME: support max packet size override */
 
-    enum usb_dw_epdir epdir = (EP_DIR(endpoint) == DIR_IN) ? USB_DW_EPDIR_IN : USB_DW_EPDIR_OUT;
-    struct usb_dw_ep* dw_ep = usb_dw_get_ep(EP_NUM(endpoint), epdir);
+    const uint8_t epnum = EP_NUM(ep);
+    const uint8_t epdir = EP_DIR(ep);
+
+    if(ep == EP_CONTROL)
+    {
+        return false;
+    }
+
+    enum usb_dw_epdir dwdir = epdir == DIR_IN ? USB_DW_EPDIR_IN : USB_DW_EPDIR_OUT;
+    if(!(usb_endpoints & (1 << (epnum + USB_DW_DIR_OFF(dwdir)))))
+    {
+        return false;
+    }
+
+    bool need_fifo = epdir == DIR_IN;
+#ifdef USB_DW_SHARED_FIFO
+    /* in shared fifo mode, only periodic endpoints need dedicated fifo */
+    need_fifo &= type == USB_ENDPOINT_XFER_ISOC || type == USB_ENDPOINT_XFER_INT;
+#endif
+    if(!need_fifo)
+    {
+        goto ok;
+    }
+
+    for (int fnum = 1; fnum <= n_ptxfifos; fnum++)
+    {
+        if (~ctx->txfifo_usage & (1 << fnum))
+        {
+            ctx->txfifo_usage |= 1 << fnum;
+            ctx->assigned_txfifos[epnum] = fnum;
+            goto ok;
+        }
+    }
+
+    return false;
+
+ok:
+    ctx->type[epnum][epdir] = type;
+    return true;
+}
+
+void usb_drv_ep_init(const struct usb_drv_ep_alloc_ctx* ctx, int ep)
+{
+    /* FIXME: support max packet size override */
+    const int epnum = EP_NUM(ep);
+    const int epdir_ = EP_DIR(ep);
+    const int type = ctx->type[epnum][epdir_];
+
+    enum usb_dw_epdir epdir = (epdir_ == DIR_IN) ? USB_DW_EPDIR_IN : USB_DW_EPDIR_OUT;
+    struct usb_dw_ep* dw_ep = usb_dw_get_ep(EP_NUM(ep), epdir);
 
     int maxpktsize;
     if(type == EPTYP_ISOCHRONOUS)
@@ -1635,32 +1653,23 @@ int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size)
     }
 
     usb_dw_target_disable_irq();
-    int res = usb_dw_configure_ep(EP_NUM(endpoint), epdir, type, maxpktsize);
+    usb_dw_configure_ep(ctx, epnum, epdir, type, maxpktsize);
     usb_dw_target_enable_irq();
 
-    if(res >= 0)
-    {
-        dw_ep->active = true;
-        return 0;
-    }
-    else
-    {
-        return -1;
-    }
+    dw_ep->active = true;
 }
 
-int usb_drv_deinit_endpoint(int endpoint)
+void usb_drv_ep_deinit(const struct usb_drv_ep_alloc_ctx* ctx, int ep)
 {
-    enum usb_dw_epdir epdir = (EP_DIR(endpoint) == DIR_IN) ? USB_DW_EPDIR_IN : USB_DW_EPDIR_OUT;
-    struct usb_dw_ep* dw_ep = usb_dw_get_ep(EP_NUM(endpoint), epdir);
+    (void)ctx;
+    enum usb_dw_epdir epdir = (EP_DIR(ep) == DIR_IN) ? USB_DW_EPDIR_IN : USB_DW_EPDIR_OUT;
+    struct usb_dw_ep* dw_ep = usb_dw_get_ep(EP_NUM(ep), epdir);
 
     usb_dw_target_disable_irq();
-    usb_dw_unconfigure_ep(EP_NUM(endpoint), epdir);
+    usb_dw_unconfigure_ep(EP_NUM(ep), epdir);
     usb_dw_target_enable_irq();
 
     dw_ep->active = false;
-
-    return 0;
 }
 
 int usb_drv_recv_nonblocking(int endpoint, void* ptr, int length)
diff --git a/firmware/export/usb-designware.h b/firmware/export/usb-designware.h
index ccef6d3d0d..424390e3b5 100644
--- a/firmware/export/usb-designware.h
+++ b/firmware/export/usb-designware.h
@@ -25,6 +25,7 @@
 
 #include <inttypes.h>
 #include "config.h"
+#include "cpu.h"
 
 #ifndef REG32_PTR_T
 #define REG32_PTR_T volatile uint32_t *
@@ -288,4 +289,13 @@ extern void usb_dw_target_enable_irq(void);
 extern void usb_dw_target_disable_irq(void);
 extern void usb_dw_target_clear_irq(void);
 
+/* endpoint allocation */
+struct usb_drv_ep_alloc_ctx_dw
+{
+    int8_t type[USB_NUM_ENDPOINTS][2];
+    uint16_t txfifo_usage;
+    uint8_t assigned_txfifos[USB_NUM_ENDPOINTS];
+};
+#define usb_drv_ep_alloc_ctx usb_drv_ep_alloc_ctx_dw
+
 #endif /* __USB_DESIGNWARE_H__ */
diff --git a/firmware/export/usb_drv.h b/firmware/export/usb_drv.h
index 6c8949200d..405ea1d9dd 100644
--- a/firmware/export/usb_drv.h
+++ b/firmware/export/usb_drv.h
@@ -62,19 +62,42 @@ enum usb_control_response {
     USB_CONTROL_RECEIVE,
 };
 
+/* endpoint allocation:
+ * there are two ways to implement endpoint allocation.
+ * 1. define usb_drv_ep_specs and usb_drv_ep_specs_flags in the driver.
+ *    this is the simplest option when the types accepted by each endpoint are mutually independent.
+ *    these variables are set only once during driver initialization and should not be modified afterward.
+ * 2. define usb_drv_ep_alloc_ctx and implement usb_drv_ep_reset_alloc_ctx and usb_drv_ep_allocate in the driver.
+ *    if the available endpoint types change based on allocation status,
+ *    these functions can be overridden to allow the driver to track the endpoint state.
+ * */
+
+#ifndef usb_drv_ep_alloc_ctx
+/* option 1 */
 #define USB_ENDPOINT_TYPE_ANY  (-1)
 #define USB_ENDPOINT_TYPE_NONE (-2)
-
 struct usb_drv_ep_spec {
     int8_t type[2]; /* USB_ENDPOINT_TYPE_{ANY,NONE} USB_ENDPOINT_XFER_* */
 };
-
 extern struct usb_drv_ep_spec usb_drv_ep_specs[USB_NUM_ENDPOINTS];
 
 #define USB_ENDPOINT_SPEC_FORCE_IO_TYPE_MATCH (1 << 0)
 #define USB_ENDPOINT_SPEC_IO_EXCLUSIVE        (1 << 1)
 extern uint8_t usb_drv_ep_specs_flags;
 
+struct usb_drv_ep_alloc_ctx {
+    int8_t type[USB_NUM_ENDPOINTS][2];
+    int max_packet_size[USB_NUM_ENDPOINTS][2];
+};
+#else
+/* option 2 */
+void usb_drv_ep_reset_alloc_ctx(struct usb_drv_ep_alloc_ctx* ctx);
+bool usb_drv_ep_allocate(struct usb_drv_ep_alloc_ctx* ctx, int ep, int type, int max_packet_size);
+#endif
+
+void usb_drv_ep_init(const struct usb_drv_ep_alloc_ctx* ctx, int ep);
+void usb_drv_ep_deinit(const struct usb_drv_ep_alloc_ctx* ctx, int ep);
+
 /* one-time initialisation of the USB driver */
 void usb_drv_startup(void);
 void usb_drv_int_enable(bool enable); /* Target implemented */
@@ -98,8 +121,6 @@ int usb_drv_port_speed(void);
 void usb_drv_cancel_all_transfers(void);
 void usb_drv_set_test_mode(int mode);
 bool usb_drv_connected(void);
-int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size);
-int usb_drv_deinit_endpoint(int endpoint);
 #ifdef USB_HAS_ISOCHRONOUS
 /* returns the last received frame number (the 11-bit number contained in the last SOF):
  * - full-speed: the host sends one SOF every 1ms (so 1000 SOF/s)
diff --git a/firmware/target/arm/as3525/usb-drv-as3525.c b/firmware/target/arm/as3525/usb-drv-as3525.c
index 863b553164..56216bec5e 100644
--- a/firmware/target/arm/as3525/usb-drv-as3525.c
+++ b/firmware/target/arm/as3525/usb-drv-as3525.c
@@ -343,37 +343,37 @@ int usb_drv_port_speed(void)
     return (USB_DEV_STS & USB_DEV_STS_MASK_SPD) ? 0 : 1;
 }
 
-int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size) {
-    (void)max_packet_size;
-
-    int i = EP_NUM(endpoint);
-//    int d = EP_DIR(endpoint) == DIR_IN ? 0 : 1;
-
-    if (EP_DIR(endpoint) == DIR_IN) {
-        USB_IEP_CTRL(i) = USB_EP_CTRL_FLUSH |
-                          USB_EP_CTRL_SNAK  |
-                          USB_EP_CTRL_ACT   |
-                          (type << 4);
-        USB_DEV_EP_INTR_MASK &= ~(1<<i);
+void usb_drv_ep_init(const struct usb_drv_ep_alloc_ctx* ctx, int ep)
+{
+    /* FIXME: support max packet size override */
+    const int epnum = EP_NUM(ep);
+    const int epdir = EP_DIR(ep);
+    const int type = ctx->type[epnum][epdir];
+
+    if (epdir == DIR_IN) {
+        USB_IEP_CTRL(epnum) = USB_EP_CTRL_FLUSH |
+                              USB_EP_CTRL_SNAK  |
+                              USB_EP_CTRL_ACT   |
+                              (type << 4);
+        USB_DEV_EP_INTR_MASK &= ~(1<<epnum);
     } else {
-        USB_OEP_CTRL(i) = USB_EP_CTRL_FLUSH |
-                          USB_EP_CTRL_SNAK  |
-                          USB_EP_CTRL_ACT   |
-                          (type << 4);
-        USB_DEV_EP_INTR_MASK &= ~(1<<(16+i));
+        USB_OEP_CTRL(epnum) = USB_EP_CTRL_FLUSH |
+                              USB_EP_CTRL_SNAK  |
+                              USB_EP_CTRL_ACT   |
+                              (type << 4);
+        USB_DEV_EP_INTR_MASK &= ~(1<<(16+epnum));
     }
-    return 0;
 }
 
-int usb_drv_deinit_endpoint(int endpoint) {
-    int i = EP_NUM(endpoint);
-    int d = EP_DIR(endpoint) == DIR_IN ? 0 : 1;
+void usb_drv_ep_deinit(const struct usb_drv_ep_alloc_ctx* ctx, int ep)
+{
+    (void)ctx;
+    int i = EP_NUM(ep);
+    int d = EP_DIR(ep) == DIR_IN ? 0 : 1;
 
     endpoints[i][d].state = 0;
     USB_DEV_EP_INTR_MASK |= (1<<(16*d+i));
     USB_EP_CTRL(i, !d) = USB_EP_CTRL_FLUSH | USB_EP_CTRL_SNAK;
-
-    return 0;
 }
 
 void usb_drv_cancel_all_transfers(void)
diff --git a/firmware/target/arm/rk27xx/usb-drv-rk27xx.c b/firmware/target/arm/rk27xx/usb-drv-rk27xx.c
index da0fc0ffb6..2ea7395e01 100644
--- a/firmware/target/arm/rk27xx/usb-drv-rk27xx.c
+++ b/firmware/target/arm/rk27xx/usb-drv-rk27xx.c
@@ -280,27 +280,30 @@ int usb_drv_port_speed(void)
     return (DEV_INFO & DEV_SPEED) ? 0 : 1;
 }
 
-int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size) {
-    (void)max_packet_size; /* FIXME: support max packet size override */
+void usb_drv_ep_init(const struct usb_drv_ep_alloc_ctx* ctx, int ep)
+{
+    /* FIXME: support max packet size override */
+    (void)ctx;
 
-    int num = EP_NUM(endpoint);
-//    int dir = EP_DIR(endpoint);
-    (void)type;
+    const int epnum = EP_NUM(ep);
+    const int epdir = EP_DIR(ep);
 
-    struct endpoint_t *endp = &endpoints[num];
+    struct endpoint_t *endp = &endpoints[epnum];
 
-    if(EP_DIR(endpoint) == DIR_IN)
-        TXCON(endp) = (num << 8) | TXEPEN | TXNAK | TXACKINTEN | TXCFINTE;
+    if(epdir == DIR_IN)
+        TXCON(endp) = (epnum << 8) | TXEPEN | TXNAK | TXACKINTEN | TXCFINTE;
     else
-        RXCON(endp) = (num << 8) | RXEPEN | RXNAK | RXACKINTEN | RXCFINTE | RXERRINTEN;
-    EN_INT |= 1 << (num + 7);
+        RXCON(endp) = (epnum << 8) | RXEPEN | RXNAK | RXACKINTEN | RXCFINTE | RXERRINTEN;
+    EN_INT |= 1 << (epnum + 7);
 
     return 0;
 }
 
-int usb_drv_deinit_endpoint(int endpoint) {
-    int num = EP_NUM(endpoint);
-//    struct endpoint_t *endp = &endpoints[num];
+void usb_drv_ep_deinit(const struct usb_drv_ep_alloc_ctx* ctx, int ep)
+{
+    (void)ctx;
+
+    int num = EP_NUM(ep);
 
     /* disable interrupt from this endpoint */
     EN_INT &= ~(1 << (num + 7));
diff --git a/firmware/target/arm/tms320dm320/sansa-connect/tnetv105_usb_drv.c b/firmware/target/arm/tms320dm320/sansa-connect/tnetv105_usb_drv.c
index 28be80aa96..abfbf0d233 100644
--- a/firmware/target/arm/tms320dm320/sansa-connect/tnetv105_usb_drv.c
+++ b/firmware/target/arm/tms320dm320/sansa-connect/tnetv105_usb_drv.c
@@ -1489,17 +1489,21 @@ void usb_drv_set_test_mode(int mode)
     tnetv_usb_reg_write(TNETV_USB_CTRL, usbCtrl.val);
 }
 
-int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size) {
-    (void)max_packet_size; /* FIXME: support max packet size override */
-    (void)type;
+void usb_drv_ep_init(const struct usb_drv_ep_alloc_ctx* ctx, int ep)
+{
+    /* FIXME: support max packet size override */
+    (void)ctx;
 
-    int num = EP_NUM(endpoint);
-    int dir = EP_DIR(endpoint);
+    int num = EP_NUM(ep);
+    int dir = EP_DIR(ep);
     return tnetv_gadget_ep_enable(num, dir == DIR_IN);
 }
 
-int usb_drv_deinit_endpoint(int endpoint) {
-    int num = EP_NUM(endpoint);
-    int dir = EP_DIR(endpoint);
+void usb_drv_ep_deinit(const struct usb_drv_ep_alloc_ctx* ctx, int ep)
+{
+    (void)ctx;
+
+    int num = EP_NUM(ep);
+    int dir = EP_DIR(ep);
     return tnetv_gadget_ep_disable(num, dir == DIR_IN);
 }
diff --git a/firmware/target/arm/usb-drv-arc.c b/firmware/target/arm/usb-drv-arc.c
index 09c1ade438..cd78de764f 100644
--- a/firmware/target/arm/usb-drv-arc.c
+++ b/firmware/target/arm/usb-drv-arc.c
@@ -451,6 +451,42 @@ void usb_drv_startup(void)
        ((type) == USB_ENDPOINT_XFER_INT ? "INTR" : "INVL"))))
 #endif
 
+static void init_endpoint(int ep, int type, int mps) {
+    const int ep_num = EP_NUM(ep);
+    const int ep_dir = EP_DIR(ep);
+
+    logf("ep init: %d %s %s", ep_num, XFER_DIR_STR(ep_dir), XFER_TYPE_STR(type));
+
+    struct queue_head* qh;
+    unsigned int ctrl = REG_ENDPTCTRL(ep_num);
+    if(ep_dir == DIR_IN) {
+        ctrl &= ~EPCTRL_TX_TYPE;
+        ctrl |= EPCTRL_TX_DATA_TOGGLE_RST | EPCTRL_TX_ENABLE | type << EPCTRL_TX_EP_TYPE_SHIFT;
+        qh = &qh_array[ep_num * 2 + 1];
+    } else {
+        ctrl &= ~EPCTRL_RX_TYPE;
+        ctrl |= EPCTRL_RX_DATA_TOGGLE_RST | EPCTRL_RX_ENABLE | type << EPCTRL_RX_EP_TYPE_SHIFT;
+        qh = &qh_array[ep_num * 2];
+    }
+    REG_ENDPTCTRL(ep_num) = ctrl;
+
+    if(mps == -1) {
+        if(type == USB_ENDPOINT_XFER_ISOC) {
+            mps = 1024;
+        } else {
+            mps = usb_drv_port_speed() ? 512 : 64;
+        }
+    }
+    if(type == USB_ENDPOINT_XFER_ISOC)
+        /* FIXME: we can adjust the number of packets per frame, currently use one */
+        qh->max_pkt_length = mps << QH_MAX_PKT_LEN_POS | QH_ZLT_SEL | 1 << QH_MULT_POS;
+    else
+        qh->max_pkt_length = mps << QH_MAX_PKT_LEN_POS | QH_ZLT_SEL;
+
+    qh->dtd.next_td_ptr = QH_NEXT_TERMINATE;
+}
+
+
 /* manual: 32.14.1 Device Controller Initialization */
 void usb_drv_init(void)
 {
@@ -494,8 +530,8 @@ void usb_drv_init(void)
      * will cause undefined behavior for the data pid tracking on the active
      * endpoint/direction. */
     for(int ep_num=1;ep_num<USB_NUM_ENDPOINTS;ep_num++) {
-        usb_drv_init_endpoint(ep_num | USB_DIR_IN, USB_ENDPOINT_XFER_BULK, -1);
-        usb_drv_init_endpoint(ep_num | USB_DIR_OUT, USB_ENDPOINT_XFER_BULK, -1);
+        init_endpoint(ep_num | USB_DIR_IN, USB_ENDPOINT_XFER_BULK, -1);
+        init_endpoint(ep_num | USB_DIR_OUT, USB_ENDPOINT_XFER_BULK, -1);
     }
 }
 
@@ -984,46 +1020,16 @@ void usb_drv_cancel_all_transfers(void)
     }
 }
 
-int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size) {
-    int ep_num = EP_NUM(endpoint);
-    int ep_dir = EP_DIR(endpoint);
-
-    logf("ep init: %d %s %s", ep_num, XFER_DIR_STR(ep_dir), XFER_TYPE_STR(type));
-
-    struct queue_head* qh;
-    unsigned int ctrl = REG_ENDPTCTRL(ep_num);
-    if(ep_dir == DIR_IN) {
-        ctrl &= ~EPCTRL_TX_TYPE;
-        ctrl |= EPCTRL_TX_DATA_TOGGLE_RST | EPCTRL_TX_ENABLE | type << EPCTRL_TX_EP_TYPE_SHIFT;
-        qh = &qh_array[ep_num * 2 + 1];
-    } else {
-        ctrl &= ~EPCTRL_RX_TYPE;
-        ctrl |= EPCTRL_RX_DATA_TOGGLE_RST | EPCTRL_RX_ENABLE | type << EPCTRL_RX_EP_TYPE_SHIFT;
-        qh = &qh_array[ep_num * 2];
-    }
-    REG_ENDPTCTRL(ep_num) = ctrl;
-
-    if(max_packet_size == -1) {
-        if(type == USB_ENDPOINT_XFER_ISOC) {
-            max_packet_size = 1024;
-        } else {
-            max_packet_size = usb_drv_port_speed() ? 512 : 64;
-        }
-    }
-    if(type == USB_ENDPOINT_XFER_ISOC)
-        /* FIXME: we can adjust the number of packets per frame, currently use one */
-        qh->max_pkt_length = max_packet_size << QH_MAX_PKT_LEN_POS | QH_ZLT_SEL | 1 << QH_MULT_POS;
-    else
-        qh->max_pkt_length = max_packet_size << QH_MAX_PKT_LEN_POS | QH_ZLT_SEL;
-
-    qh->dtd.next_td_ptr = QH_NEXT_TERMINATE;
-
-    return 0;
+void usb_drv_ep_init(const struct usb_drv_ep_alloc_ctx* ctx, int ep) {
+    const int ep_num = EP_NUM(ep);
+    const int ep_dir = EP_DIR(ep);
+    init_endpoint(ep, ctx->type[ep_num][ep_dir], ctx->max_packet_size[ep_num][ep_dir]);
 }
 
-int usb_drv_deinit_endpoint(int endpoint) {
-    int ep_num = EP_NUM(endpoint);
-    int ep_dir = EP_DIR(endpoint);
+void usb_drv_ep_deinit(const struct usb_drv_ep_alloc_ctx* ctx, int ep) {
+    (void)ctx;
+    int ep_num = EP_NUM(ep);
+    int ep_dir = EP_DIR(ep);
 
     logf("ep deinit: %d %s", ep_num, XFER_DIR_STR(ep_dir));
 
@@ -1032,8 +1038,6 @@ int usb_drv_deinit_endpoint(int endpoint) {
     } else {
         REG_ENDPTCTRL(ep_num) &= ~EPCTRL_RX_ENABLE & ~EPCTRL_RX_TYPE;
     }
-
-    return 0;
 }
 
 static void prepare_td(struct transfer_descriptor* td,
diff --git a/firmware/target/arm/usb-s3c6400x.c b/firmware/target/arm/usb-s3c6400x.c
index e570d07e31..c26072d70c 100644
--- a/firmware/target/arm/usb-s3c6400x.c
+++ b/firmware/target/arm/usb-s3c6400x.c
@@ -588,19 +588,23 @@ void INT_USB_FUNC(void)
     GINTSTS = sts;
 }
 
-int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size) {
-    (void)max_packet_size; /* FIXME: support max packet size override */
+void usb_drv_ep_init(const struct usb_drv_ep_alloc_ctx* ctx, int ep)
+{
+    /* FIXME: support max packet size override */
+    (void)ctx;
+
+    const int epnum = EP_NUM(ep);
+    const int epdir = EP_DIR(ep);
+    const int type = ctx->type[epnum][epdir];
 
-    int num = EP_NUM(endpoint);
-    int dir = EP_DIR(endpoint);
-    bool out = dir == DIR_OUT;
-    DEPCTL(num, out) = (DEPCTL(num, out) & ~(DEPCTL_eptype_bits << DEPCTL_eptype_bitp))
+    bool out = epdir == DIR_OUT;
+    DEPCTL(epnum, out) = (DEPCTL(epnum, out) & ~(DEPCTL_eptype_bits << DEPCTL_eptype_bitp))
             | DEPCTL_setd0pid | (type << DEPCTL_eptype_bitp) | DEPCTL_usbactep;
-    return 0;
 }
 
-int usb_drv_deinit_endpoint(int endpoint) {
-    return 0;
+void usb_drv_ep_deinit(const struct usb_drv_ep_alloc_ctx* ctx, int ep)
+{
+    (void)ctx;
 }
 
 void usb_drv_cancel_all_transfers()
diff --git a/firmware/target/arm/usb-tcc.c b/firmware/target/arm/usb-tcc.c
index ba055b4cb7..ab2260e48e 100644
--- a/firmware/target/arm/usb-tcc.c
+++ b/firmware/target/arm/usb-tcc.c
@@ -100,16 +100,17 @@ static struct tcc_ep tcc_endpoints[] = {
 static bool usb_drv_write_ep(struct tcc_ep *ep);
 static void usb_set_speed(int);
 
-int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size) {
-    (void)max_packet_size; /* FIXME: support max packet size override */
-
-    tcc_endpoints[EP_NUM(endpoint)].dir = EP_DIR(endpoint) == DIR_IN ? USB_DIR_IN : USB_DIR_OUT;
-    return 0;
+void usb_drv_ep_init(const struct usb_drv_ep_alloc_ctx* ctx, int ep)
+{
+    /* FIXME: support max packet size override */
+    (void)ctx;
+    tcc_endpoints[EP_NUM(ep)].dir = EP_DIR(ep) == DIR_IN ? USB_DIR_IN : USB_DIR_OUT;
 }
 
-int usb_drv_deinit_endpoint(int endpoint) {
-    tcc_endpoints[EP_NUM(endpoint)].dir = -1;
-    return 0;
+void usb_drv_ep_deinit(const struct usb_drv_ep_alloc_ctx* ctx, int ep)
+{
+    (void)ctx;
+    tcc_endpoints[EP_NUM(ep)].dir = -1;
 }
 
 static inline void pullup_on(void)
diff --git a/firmware/target/mips/ingenic_jz47xx/usb-jz4740.c b/firmware/target/mips/ingenic_jz47xx/usb-jz4740.c
index c923d8d97a..13542b0a5d 100644
--- a/firmware/target/mips/ingenic_jz47xx/usb-jz4740.c
+++ b/firmware/target/mips/ingenic_jz47xx/usb-jz4740.c
@@ -836,14 +836,15 @@ void usb_drv_cancel_all_transfers(void)
     restore_irq(flags);
 }
 
-int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size) {
-    (void)endpoint;
-    (void)type;
-    (void)max_packet_size; /* FIXME: support max packet size override */
-    return 0;
+void usb_drv_ep_init(const struct usb_drv_ep_alloc_ctx* ctx, int ep)
+{
+    /* FIXME: support max packet size override */
+    (void)ctx;
+    (void)ep;
 }
 
-int usb_drv_deinit_endpoint(int endpoint) {
-    (void)endpoint;
-    return 0;
+void usb_drv_ep_deinit(const struct usb_drv_ep_alloc_ctx* ctx, int ep)
+{
+    (void)ctx;
+    (void)ep;
 }
diff --git a/firmware/target/mips/ingenic_jz47xx/usb-jz4760.c b/firmware/target/mips/ingenic_jz47xx/usb-jz4760.c
index 1e44a0a7aa..b8f4e05070 100644
--- a/firmware/target/mips/ingenic_jz47xx/usb-jz4760.c
+++ b/firmware/target/mips/ingenic_jz47xx/usb-jz4760.c
@@ -1192,12 +1192,12 @@ void usb_drv_cancel_all_transfers(void)
     restore_irq(flags);
 }
 
-int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size) {
-    (void)max_packet_size; /* FIXME: support max packet size override */
-    (void)type;
-
-    int num = EP_NUM(endpoint);
-    int dir = EP_DIR(endpoint);
+void usb_drv_ep_init(const struct usb_drv_ep_alloc_ctx* ctx, int ep)
+{
+    /* FIXME: support max packet size override */
+    (void)ctx;
+    int num = EP_NUM(ep);
+    int dir = EP_DIR(ep);
     int index = num * 2 + (dir == DIR_OUT ? 1 : 0);
     endpoints[index].allocated = true;
     if(dir == DIR_IN)
@@ -1207,9 +1207,11 @@ int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size) {
     return 0;
 }
 
-int usb_drv_deinit_endpoint(int endpoint) {
-    int num = EP_NUM(endpoint);
-    int dir = EP_DIR(endpoint);
+void usb_drv_ep_deinit(const struct usb_drv_ep_alloc_ctx* ctx, int ep)
+{
+    (void)ctx;
+    int num = EP_NUM(ep);
+    int dir = EP_DIR(ep);
     int index = num * 2 + (dir == DIR_OUT ? 1 : 0);
     endpoints[index].allocated = false;
     if(dir == DIR_IN)
diff --git a/firmware/usbstack/usb_core.c b/firmware/usbstack/usb_core.c
index 8b7e4510fd..c498ee4cbf 100644
--- a/firmware/usbstack/usb_core.c
+++ b/firmware/usbstack/usb_core.c
@@ -28,7 +28,6 @@
 
 #include "usb.h"
 #include "usb_ch9.h"
-#include "usb_drv.h"
 #include "usb_core.h"
 #include "usb_class_driver.h"
 
@@ -57,6 +56,12 @@
 #include "usb_iap.h"
 #endif
 
+/* include order matters, include driver header before usb_drv.h */
+#if CONFIG_USBOTG == USBOTG_DESIGNWARE
+#include "usb-designware.h"
+#endif
+#include "usb_drv.h"
+
 /* TODO: Move target-specific stuff somewhere else (serial number reading) */
 
 #if defined(IPOD_ARCH) && defined(CPU_PP)
@@ -179,8 +184,6 @@ static int usb_no_host_callback(struct timeout *tmo)
 }
 #endif
 
-static int usb_core_num_interfaces[NUM_CONFIGS];
-
 typedef void (*completion_handler_t)(int ep, int dir, int status, int length);
 typedef bool (*fast_completion_handler_t)(int ep, int dir, int status, int length);
 typedef bool (*control_handler_t)(struct usb_ctrlrequest* req, void* reqdata,
@@ -199,7 +202,13 @@ struct ep_alloc_state {
     struct usb_class_driver* owner[2];
 };
 
-static struct ep_alloc_state ep_alloc_states[NUM_CONFIGS][USB_NUM_ENDPOINTS];
+struct config_state {
+    struct usb_drv_ep_alloc_ctx ep_alloc_ctx;
+    struct ep_alloc_state ep_alloc_states[USB_NUM_ENDPOINTS];
+    uint8_t num_interfaces;
+};
+
+struct config_state config_states[NUM_CONFIGS];
 
 static struct usb_class_driver* drivers[USB_NUM_DRIVERS] =
 {
@@ -407,9 +416,6 @@ void usb_core_init(void)
         }
     }
 
-    /* clear endpoint allocation state */
-    memset(ep_alloc_states, 0, sizeof(ep_alloc_states));
-
     initialized = true;
     usb_state = DEFAULT;
     usb_config = 0;
@@ -548,52 +554,86 @@ static void usb_core_set_serial_function_id(void)
 }
 
 /* synchronize endpoint initialization state to allocation state */
-static void init_deinit_endpoints(uint8_t conf_index, bool init) {
+static void init_deinit_endpoints(int config, bool init) {
     for(int epnum = 0; epnum < USB_NUM_ENDPOINTS; epnum += 1) {
         for(int dir = 0; dir < 2; dir += 1) {
-            struct ep_alloc_state* alloc = &ep_alloc_states[conf_index][epnum];
-            struct usb_class_driver* driver = alloc->owner[dir];
+            struct config_state* cstate = &config_states[config - 1];
+            struct ep_alloc_state* astate = &cstate->ep_alloc_states[epnum];
+            struct usb_class_driver* driver = astate->owner[dir];
             if(driver == NULL) {
                 continue;
             }
             int ep = epnum | (dir == DIR_OUT ? USB_DIR_OUT : USB_DIR_IN);
-            int ret;
-            if(init) {
-                int ps = driver->get_max_packet_size ? driver->get_max_packet_size(ep) : -1;
-                ret = usb_drv_init_endpoint(ep, alloc->type[dir], ps);
-            } else {
-                ret = usb_drv_deinit_endpoint(ep);
-            }
-            if(ret) {
-                logf("usb_core: usb_drv_%s_endpoint failed ep=%d dir=%d", init ? "init" : "deinit", epnum, dir);
-                continue;
-            }
             if(init) {
+                usb_drv_ep_init(&cstate->ep_alloc_ctx, ep);
                 ep_data[epnum].completion_handler[dir] = driver->transfer_complete;
                 ep_data[epnum].fast_completion_handler[dir] = driver->fast_transfer_complete;
                 ep_data[epnum].control_handler[dir] = driver->control_request;
+            } else {
+                usb_drv_ep_deinit(&cstate->ep_alloc_ctx, ep);
             }
         }
     }
 }
 
+#ifndef usb_drv_ep_alloc_ctx
+/* default endpoint allocator using usb_drv_ep_specs table */
+static void usb_drv_ep_reset_alloc_ctx(struct usb_drv_ep_alloc_ctx* ctx) {
+    for(int i = 0; i < USB_NUM_ENDPOINTS; i += 1) {
+        ctx->type[i][0] = -1;
+        ctx->type[i][1] = -1;
+    }
+}
+
+static bool usb_drv_ep_allocate(struct usb_drv_ep_alloc_ctx* ctx, int ep, int type, int max_packet_size) {
+    const uint8_t epnum = EP_NUM(ep);
+    const uint8_t epdir = EP_DIR(ep);
+
+    struct usb_drv_ep_spec* spec = &usb_drv_ep_specs[epnum];
+
+    const int8_t spec_type = spec->type[epdir];
+    if(spec_type != type && spec_type != USB_ENDPOINT_TYPE_ANY) {
+        return false;
+    }
+
+    const int8_t other_type = ctx->type[epnum][!epdir];
+    if(usb_drv_ep_specs_flags & USB_ENDPOINT_SPEC_IO_EXCLUSIVE && other_type != -1) {
+        /* the other side is allocated */
+        return false;
+    }
+
+    if(usb_drv_ep_specs_flags & USB_ENDPOINT_SPEC_FORCE_IO_TYPE_MATCH && other_type != -1 && other_type != type) {
+        /* the other side is allocated with another type */
+        return false;
+    }
+
+    ctx->type[epnum][epdir] = type;
+    ctx->max_packet_size[epnum][epdir] = max_packet_size;
+
+    return true;
+}
+#endif
+
 static void allocate_interfaces_and_endpoints(void)
 {
     if(usb_config != 0) {
         /* deinit currently used endpoints */
-        init_deinit_endpoints(usb_config - 1, false);
+        init_deinit_endpoints(usb_config, false);
     }
 
+retry:
     /* reset allocations */
-    memset(ep_alloc_states, 0, sizeof(ep_alloc_states));
-
-    int interface[NUM_CONFIGS] = {0};
+    for(int i = 0; i < NUM_CONFIGS; i += 1) {
+        config_states[i].num_interfaces = 0;
+        memset(config_states[i].ep_alloc_states, 0, sizeof(config_states[i].ep_alloc_states));
+        usb_drv_ep_reset_alloc_ctx(&config_states[i].ep_alloc_ctx);
+    }
 
     for(int i = 0; i < USB_NUM_DRIVERS; i++) {
         struct usb_class_driver* driver = drivers[i];
-        const uint8_t conf_index = driver->config - 1;
+        struct config_state* cstate = &config_states[driver->config - 1];
 
-        if(!driver->enabled) {
+        if(!driver->enabled || driver->error) {
             continue;
         }
 
@@ -603,69 +643,38 @@ static void allocate_interfaces_and_endpoints(void)
             struct usb_class_driver_ep_allocation* req = &driver->ep_allocs[reqnum];
             req->ep = 0;
             for(int epnum = 1; epnum < USB_NUM_ENDPOINTS; epnum += 1) {
-                struct usb_drv_ep_spec* spec = &usb_drv_ep_specs[epnum];
-                /* ep type check */
-                const int8_t spec_type = spec->type[req->dir];
-                if(spec_type != req->type && spec_type != USB_ENDPOINT_TYPE_ANY) {
-                    continue;
-                }
                 /* free check */
-                struct ep_alloc_state* alloc = &ep_alloc_states[conf_index][epnum];
+                struct ep_alloc_state* alloc = &cstate->ep_alloc_states[epnum];
                 if(alloc->owner[req->dir] != NULL) {
                     continue;
                 }
 
-                /* this ep's requested direction is free */
-
-                /* another checks */
-                if(usb_drv_ep_specs_flags & USB_ENDPOINT_SPEC_IO_EXCLUSIVE) {
-                    /* check for the other direction type */
-                    if(alloc->owner[!req->dir] != NULL) {
-                        /* the other side is allocated */
-                        continue;
-                    }
-                }
-                if(usb_drv_ep_specs_flags & USB_ENDPOINT_SPEC_FORCE_IO_TYPE_MATCH) {
-                    /* check for other direction type */
-                    if(alloc->owner[!req->dir] != NULL && alloc->type[!req->dir] != req->type) {
-                        /* the other side is allocated with another type */
-                        continue;
-                    }
+                /* driver specific check */
+                const int ep = epnum | (req->dir == DIR_OUT ? USB_DIR_OUT : USB_DIR_IN);
+                const int ps = driver->get_max_packet_size ? driver->get_max_packet_size(ep) : -1;
+                if(!usb_drv_ep_allocate(&cstate->ep_alloc_ctx, ep, req->type, ps)) {
+                    continue;
                 }
 
                 /* all checks passed, assign it */
-                const int ep = epnum | (req->dir == DIR_OUT ? USB_DIR_OUT : USB_DIR_IN);
                 req->ep = ep;
                 alloc->owner[req->dir] = driver;
                 alloc->type[req->dir] = req->type;
                 break;
             }
             if(req->ep == 0 && !req->optional) {
-                /* no matching ep found, disable the driver */
+                /* no matching ep found, retry allocation excluding this driver */
+                logf("usb_core: no endpoint allocated for driver %d", i);
                 driver->enabled = false;
-                /* also revert all allocations for this driver */
-                for(reqnum = reqnum - 1; reqnum >= 0; reqnum -= 1) {
-                    const uint8_t ep = driver->ep_allocs[reqnum].ep;
-                    const uint8_t epnum = EP_NUM(ep);
-                    const uint8_t epdir = EP_DIR(ep);
-                    const uint8_t dir = epdir == USB_DIR_OUT ? DIR_OUT : DIR_IN;
-                    ep_alloc_states[conf_index][epnum].owner[dir] = NULL;
-                }
-                break;
+                goto retry;
             }
         }
 
-        if(!driver->enabled) {
-            continue;
-        }
-
         /* assign interfaces */
-        driver->first_interface = interface[conf_index];
-        interface[conf_index] = driver->set_first_interface(interface[conf_index]);
-        driver->last_interface = interface[conf_index];
+        driver->first_interface = cstate->num_interfaces;
+        cstate->num_interfaces = driver->set_first_interface(cstate->num_interfaces);
+        driver->last_interface = cstate->num_interfaces;
     }
-
-    memcpy(usb_core_num_interfaces, interface, sizeof(interface));
 }
 
 
@@ -765,7 +774,7 @@ static void request_handler_device_get_descriptor(struct usb_ctrlrequest* req, v
                 }
             }
 
-            config_descriptor.bNumInterfaces = usb_core_num_interfaces[index];
+            config_descriptor.bNumInterfaces = config_states[index].num_interfaces;
             config_descriptor.bConfigurationValue = index + 1;
             config_descriptor.wTotalLength = (uint16_t)size;
             memcpy(&response_data[0], &config_descriptor, sizeof(struct usb_config_descriptor));
@@ -844,7 +853,7 @@ static int usb_core_do_set_config(uint8_t new_config)
                 drivers[i]->disconnect();
             }
         }
-        init_deinit_endpoints(usb_config - 1, false);
+        init_deinit_endpoints(usb_config, false);
 
         /* clear any pending transfer completions,
          * because they are depend on contents of ep_data */
@@ -861,7 +870,7 @@ static int usb_core_do_set_config(uint8_t new_config)
 
     /* activate new config */
     if(usb_config != 0) {
-        init_deinit_endpoints(usb_config - 1, true);
+        init_deinit_endpoints(usb_config, true);
         for(int i = 0; i < USB_NUM_DRIVERS; i++) {
             if(!is_active(drivers[i])) {
                 continue;
-- 
rockbox-cvs mailing list
[email protected]
https://lists.haxx.se/mailman/listinfo/rockbox-cvs